/src/vlc/modules/codec/cc.c
Line | Count | Source |
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 | 108 | vlc_module_begin () |
62 | 54 | set_shortname( N_("CC 608/708")) |
63 | 54 | set_description( N_("Closed Captions decoder") ) |
64 | 54 | set_capability( "spu decoder", 50 ) |
65 | 54 | set_subcategory( SUBCAT_INPUT_SCODEC ) |
66 | 54 | set_callbacks( Open, Close ) |
67 | | |
68 | 54 | add_bool( "cc-opaque", true, |
69 | 54 | OPAQUE_TEXT, OPAQUE_LONGTEXT ) |
70 | 54 | 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 | 539k | #define EIA608_SCREEN_ROWS 15 |
106 | 10.6M | #define EIA608_SCREEN_COLUMNS 32 |
107 | | |
108 | 45.5k | #define EIA608_MARGIN 0.10f |
109 | 22.7k | #define EIA608_VISIBLE (1.0f - EIA608_MARGIN * 2) |
110 | 37.1k | #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 | 5.11M | #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 | 17.5k | #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 | 15.9k | { |
257 | 15.9k | decoder_t *p_dec = (decoder_t*)p_this; |
258 | 15.9k | decoder_sys_t *p_sys; |
259 | | |
260 | 15.9k | if( ( p_dec->fmt_in->i_codec != VLC_CODEC_CEA608 || |
261 | 2.42k | p_dec->fmt_in->subs.cc.i_channel > 3 ) && |
262 | 13.5k | ( p_dec->fmt_in->i_codec != VLC_CODEC_CEA708 || |
263 | 50 | p_dec->fmt_in->subs.cc.i_channel > 63 ) ) |
264 | 13.5k | return VLC_EGENERIC; |
265 | | |
266 | 2.47k | p_dec->pf_decode = Decode; |
267 | 2.47k | p_dec->pf_flush = Flush; |
268 | | |
269 | | /* Allocate the memory needed to store the decoder's structure */ |
270 | 2.47k | p_dec->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) ); |
271 | 2.47k | if( p_sys == NULL ) |
272 | 0 | return VLC_ENOMEM; |
273 | | |
274 | 2.47k | if( p_dec->fmt_in->i_codec == VLC_CODEC_CEA608 ) |
275 | 2.42k | { |
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 | 2.42k | p_sys->i_field = p_dec->fmt_in->subs.cc.i_channel >> 1; |
281 | 2.42k | p_sys->i_channel = 1 + (p_dec->fmt_in->subs.cc.i_channel & 1); |
282 | | |
283 | 2.42k | p_sys->p_eia608 = malloc(sizeof(*p_sys->p_eia608)); |
284 | 2.42k | if( !p_sys->p_eia608 ) |
285 | 0 | { |
286 | 0 | free( p_sys ); |
287 | 0 | return VLC_ENOMEM; |
288 | 0 | } |
289 | 2.42k | Eia608Init( p_sys->p_eia608 ); |
290 | 2.42k | } |
291 | 50 | else |
292 | 50 | { |
293 | 50 | p_sys->p_dtvcc = CEA708_DTVCC_Demuxer_New( p_dec, DTVCC_ServiceData_Handler ); |
294 | 50 | if( !p_sys->p_dtvcc ) |
295 | 0 | { |
296 | 0 | free( p_sys ); |
297 | 0 | return VLC_ENOMEM; |
298 | 0 | } |
299 | | |
300 | 50 | p_sys->p_cea708 = CEA708_Decoder_New( p_dec ); |
301 | 50 | 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 | 50 | p_sys->i_channel = p_dec->fmt_in->subs.cc.i_channel; |
309 | 50 | } |
310 | | |
311 | 2.47k | p_sys->b_opaque = var_InheritBool( p_dec, "cc-opaque" ); |
312 | 2.47k | p_sys->i_reorder_depth = p_dec->fmt_in->subs.cc.i_reorder_depth; |
313 | | |
314 | 2.47k | p_dec->fmt_out.i_codec = VLC_CODEC_TEXT; |
315 | | |
316 | 2.47k | return VLC_SUCCESS; |
317 | 2.47k | } |
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 | 42.9k | { |
352 | 42.9k | block_t *p_block = Pop( p_dec, b_drain ); |
353 | 42.9k | if( !p_block ) |
354 | 31.4k | return false; |
355 | | |
356 | 11.4k | Convert( p_dec, p_block->i_pts, p_block->p_buffer, p_block->i_buffer ); |
357 | 11.4k | block_Release( p_block ); |
358 | | |
359 | 11.4k | return true; |
360 | 42.9k | } |
361 | | |
362 | | static int Decode( decoder_t *p_dec, block_t *p_block ) |
363 | 25.4k | { |
364 | 25.4k | decoder_sys_t *p_sys = p_dec->p_sys; |
365 | | |
366 | 25.4k | if( p_block ) |
367 | 11.4k | { |
368 | | /* Reset decoder if needed */ |
369 | 11.4k | 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 | 11.4k | if( p_sys->i_reorder_depth == 0 ) |
396 | 6.04k | { |
397 | | /* Wait for a P and output all *previous* picture by pts order (for |
398 | | * hierarchical B frames) */ |
399 | 6.04k | if( (p_block->i_flags & BLOCK_FLAG_TYPE_B) == 0 ) |
400 | 6.04k | for( ; DoDecode( p_dec, true ); ); |
401 | 6.04k | } |
402 | | |
403 | 11.4k | Push( p_dec, p_block ); |
404 | 11.4k | } |
405 | | |
406 | 25.4k | const bool b_no_reorder = (p_dec->fmt_in->subs.cc.i_reorder_depth < 0); |
407 | 36.8k | for( ; DoDecode( p_dec, (p_block == NULL) || b_no_reorder ); ); |
408 | | |
409 | 25.4k | return VLCDEC_SUCCESS; |
410 | 25.4k | } |
411 | | |
412 | | /***************************************************************************** |
413 | | * CloseDecoder: clean up the decoder |
414 | | *****************************************************************************/ |
415 | | static void Close( vlc_object_t *p_this ) |
416 | 2.47k | { |
417 | 2.47k | decoder_t *p_dec = (decoder_t *)p_this; |
418 | 2.47k | decoder_sys_t *p_sys = p_dec->p_sys; |
419 | | |
420 | 2.47k | free( p_sys->p_eia608 ); |
421 | 2.47k | if( p_sys->p_cea708 ) |
422 | 50 | { |
423 | 50 | CEA708_Decoder_Release( p_sys->p_cea708 ); |
424 | 50 | CEA708_DTVCC_Demuxer_Release( p_sys->p_dtvcc ); |
425 | 50 | } |
426 | | |
427 | 2.47k | block_ChainRelease( p_sys->p_queue ); |
428 | 2.47k | free( p_sys ); |
429 | 2.47k | } |
430 | | |
431 | | /***************************************************************************** |
432 | | * |
433 | | *****************************************************************************/ |
434 | | static void Push( decoder_t *p_dec, block_t *p_block ) |
435 | 11.4k | { |
436 | 11.4k | decoder_sys_t *p_sys = p_dec->p_sys; |
437 | | |
438 | 11.4k | 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 | 11.4k | block_t **pp_block; |
445 | | /* find insertion point */ |
446 | 11.4k | 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 | 11.4k | p_block->p_next = *pp_block ? *pp_block : NULL; |
463 | 11.4k | *pp_block = p_block; |
464 | 11.4k | p_sys->i_queue++; |
465 | 11.4k | } |
466 | | |
467 | | static block_t *Pop( decoder_t *p_dec, bool b_forced ) |
468 | 42.9k | { |
469 | 42.9k | decoder_sys_t *p_sys = p_dec->p_sys; |
470 | 42.9k | block_t *p_block; |
471 | | |
472 | 42.9k | if( p_sys->i_queue == 0 ) |
473 | 25.4k | return NULL; |
474 | | |
475 | 17.5k | if( !b_forced && p_sys->i_queue < CC_MAX_REORDER_SIZE ) |
476 | 6.04k | { |
477 | 6.04k | if( p_sys->i_queue < p_sys->i_reorder_depth || p_sys->i_reorder_depth == 0 ) |
478 | 6.04k | return NULL; |
479 | 6.04k | } |
480 | | |
481 | | /* dequeue head */ |
482 | 11.4k | p_block = p_sys->p_queue; |
483 | 11.4k | p_sys->p_queue = p_block->p_next; |
484 | 11.4k | p_block->p_next = NULL; |
485 | 11.4k | p_sys->i_queue--; |
486 | | |
487 | 11.4k | return p_block; |
488 | 17.5k | } |
489 | | |
490 | | static subpicture_t *Subtitle( decoder_t *p_dec, eia608_t *h, vlc_tick_t i_pts ) |
491 | 22.7k | { |
492 | | //decoder_sys_t *p_sys = p_dec->p_sys; |
493 | 22.7k | subpicture_t *p_spu = NULL; |
494 | | |
495 | | /* We cannot display a subpicture with no date */ |
496 | 22.7k | if( i_pts == VLC_TICK_INVALID ) |
497 | 0 | return NULL; |
498 | | |
499 | | /* Create the subpicture unit */ |
500 | 22.7k | p_spu = decoder_NewSubpictureText( p_dec ); |
501 | 22.7k | if( !p_spu ) |
502 | 0 | return NULL; |
503 | | |
504 | 22.7k | p_spu->i_start = i_pts; |
505 | 22.7k | p_spu->i_stop = i_pts + VLC_TICK_FROM_SEC(10); /* 10s max */ |
506 | 22.7k | p_spu->b_ephemer = true; |
507 | | |
508 | 22.7k | subtext_updater_sys_t *p_spu_sys = p_spu->updater.sys; |
509 | 22.7k | 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 | 22.7k | p_spu_sys->region.b_absolute = false; p_spu_sys->region.b_in_window = false; |
515 | 22.7k | p_spu_sys->region.align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT; |
516 | 22.7k | p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT; |
517 | 22.7k | 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 | 22.7k | p_spu_sys->p_default_style->i_style_flags |= STYLE_MONOSPACED; |
521 | 22.7k | if( p_dec_sys->b_opaque ) |
522 | 22.7k | { |
523 | 22.7k | p_spu_sys->p_default_style->i_background_alpha = STYLE_ALPHA_OPAQUE; |
524 | 22.7k | p_spu_sys->p_default_style->i_features |= STYLE_HAS_BACKGROUND_ALPHA; |
525 | 22.7k | p_spu_sys->p_default_style->i_style_flags |= STYLE_BACKGROUND; |
526 | 22.7k | } |
527 | 22.7k | p_spu_sys->margin_ratio = EIA608_MARGIN; |
528 | 22.7k | 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 | 22.7k | p_spu_sys->p_default_style->f_font_relsize = EIA608_VISIBLE * 100 / EIA608_SCREEN_ROWS / |
531 | 22.7k | FONT_TO_LINE_HEIGHT_RATIO; |
532 | 22.7k | p_spu_sys->p_default_style->i_features |= (STYLE_HAS_FONT_COLOR | STYLE_HAS_FLAGS); |
533 | | |
534 | 22.7k | Eia608FillUpdaterRegions( p_spu_sys, h ); |
535 | | |
536 | 22.7k | return p_spu; |
537 | 22.7k | } |
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 | 11.4k | { |
542 | 11.4k | decoder_sys_t *p_sys = p_dec->p_sys; |
543 | | |
544 | 11.4k | unsigned i_ticks = 0; |
545 | 151k | while( i_buffer >= 3 ) |
546 | 139k | { |
547 | 139k | if( (p_buffer[0] & 0x04) /* Valid bit */ ) |
548 | 134k | { |
549 | 134k | 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 | 134k | if ( p_sys->p_eia608 && |
552 | 133k | (p_buffer[0] & 0x03) == p_sys->i_field ) |
553 | 103k | { |
554 | 103k | eia608_status_t i_status = Eia608Parse( p_sys->p_eia608, |
555 | 103k | 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 | 103k | if( i_status & (EIA608_STATUS_DISPLAY | EIA608_STATUS_CHANGED) ) |
565 | 22.7k | { |
566 | 22.7k | Debug(printf("\n")); |
567 | 22.7k | subpicture_t *p_spu = Subtitle( p_dec, p_sys->p_eia608, i_spupts ); |
568 | 22.7k | if( p_spu ) |
569 | 22.7k | decoder_QueueSub( p_dec, p_spu ); |
570 | 22.7k | } |
571 | 103k | } |
572 | 31.1k | else if( p_sys->p_cea708 && (p_buffer[0] & 0x03) >= 2 ) |
573 | 427 | { |
574 | 427 | CEA708_DTVCC_Demuxer_Push( p_sys->p_dtvcc, i_spupts, p_buffer ); |
575 | 427 | } |
576 | 134k | } |
577 | | |
578 | 139k | i_ticks++; |
579 | | |
580 | 139k | i_buffer -= 3; |
581 | 139k | p_buffer += 3; |
582 | 139k | } |
583 | 11.4k | } |
584 | | |
585 | | |
586 | | /***************************************************************************** |
587 | | * |
588 | | *****************************************************************************/ |
589 | | static void Eia608Cursor( eia608_t *h, int dx ) |
590 | 31.4k | { |
591 | 31.4k | h->cursor.i_column += dx; |
592 | 31.4k | if( h->cursor.i_column < 0 ) |
593 | 2.80k | h->cursor.i_column = 0; |
594 | 28.6k | else if( h->cursor.i_column > EIA608_SCREEN_COLUMNS-1 ) |
595 | 5.25k | h->cursor.i_column = EIA608_SCREEN_COLUMNS-1; |
596 | 31.4k | } |
597 | | static void Eia608ClearScreenRowX( eia608_t *h, int i_screen, int i_row, int x ) |
598 | 153k | { |
599 | 153k | eia608_screen *screen = &h->screen[i_screen]; |
600 | | |
601 | 153k | if( x == 0 ) |
602 | 150k | { |
603 | 150k | screen->row_used[i_row] = false; |
604 | 150k | } |
605 | 2.37k | else |
606 | 2.37k | { |
607 | 2.37k | screen->row_used[i_row] = false; |
608 | 6.77k | for( int i = 0; i < x; i++ ) |
609 | 6.54k | { |
610 | 6.54k | if( screen->characters[i_row][i] != ' ' || |
611 | 5.75k | screen->colors[i_row][i] != EIA608_COLOR_DEFAULT || |
612 | 5.63k | screen->fonts[i_row][i] != EIA608_FONT_REGULAR ) |
613 | 2.14k | { |
614 | 2.14k | screen->row_used[i_row] = true; |
615 | 2.14k | break; |
616 | 2.14k | } |
617 | 6.54k | } |
618 | 2.37k | } |
619 | | |
620 | 5.17M | for( ; x < EIA608_SCREEN_COLUMNS+1; x++ ) |
621 | 5.02M | { |
622 | 5.02M | screen->characters[i_row][x] = x < EIA608_SCREEN_COLUMNS ? ' ' : '\0'; |
623 | 5.02M | screen->colors[i_row][x] = EIA608_COLOR_DEFAULT; |
624 | 5.02M | screen->fonts[i_row][x] = EIA608_FONT_REGULAR; |
625 | 5.02M | } |
626 | 153k | } |
627 | | |
628 | | static void Eia608ClearScreenRow( eia608_t *h, int i_screen, int i_row ) |
629 | 150k | { |
630 | 150k | Eia608ClearScreenRowX( h, i_screen, i_row, 0 ); |
631 | 150k | } |
632 | | |
633 | | static void Eia608ClearScreen( eia608_t *h, int i_screen ) |
634 | 8.47k | { |
635 | 135k | for( int i = 0; i < EIA608_SCREEN_ROWS; i++ ) |
636 | 127k | Eia608ClearScreenRow( h, i_screen, i ); |
637 | 8.47k | } |
638 | | |
639 | | static int Eia608GetWritingScreenIndex( eia608_t *h ) |
640 | 31.0k | { |
641 | 31.0k | switch( h->mode ) |
642 | 31.0k | { |
643 | 8.47k | case EIA608_MODE_POPUP: // Non displayed screen |
644 | 8.47k | return 1 - h->i_screen; |
645 | | |
646 | 2.47k | case EIA608_MODE_ROLLUP_2: // Displayed screen |
647 | 15.5k | case EIA608_MODE_ROLLUP_3: |
648 | 19.2k | case EIA608_MODE_ROLLUP_4: |
649 | 22.5k | case EIA608_MODE_PAINTON: |
650 | 22.5k | 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 | 31.0k | } |
656 | 31.0k | } |
657 | | |
658 | | static void Eia608EraseScreen( eia608_t *h, bool b_displayed ) |
659 | 3.62k | { |
660 | 3.62k | Eia608ClearScreen( h, b_displayed ? h->i_screen : (1-h->i_screen) ); |
661 | 3.62k | } |
662 | | |
663 | | static void Eia608Write( eia608_t *h, const uint8_t c ) |
664 | 27.2k | { |
665 | 27.2k | const int i_row = h->cursor.i_row; |
666 | 27.2k | const int i_column = h->cursor.i_column; |
667 | 27.2k | eia608_screen *screen; |
668 | | |
669 | 27.2k | if( h->mode == EIA608_MODE_TEXT ) |
670 | 2.99k | return; |
671 | | |
672 | 24.2k | screen = &h->screen[Eia608GetWritingScreenIndex( h )]; |
673 | | |
674 | 24.2k | screen->characters[i_row][i_column] = c; |
675 | 24.2k | screen->colors[i_row][i_column] = h->color; |
676 | 24.2k | screen->fonts[i_row][i_column] = h->font; |
677 | 24.2k | screen->row_used[i_row] = true; |
678 | 24.2k | Eia608Cursor( h, 1 ); |
679 | 24.2k | } |
680 | | static void Eia608Erase( eia608_t *h ) |
681 | 3.75k | { |
682 | 3.75k | const int i_row = h->cursor.i_row; |
683 | 3.75k | const int i_column = h->cursor.i_column - 1; |
684 | 3.75k | eia608_screen *screen; |
685 | | |
686 | 3.75k | if( h->mode == EIA608_MODE_TEXT ) |
687 | 160 | return; |
688 | 3.59k | if( i_column < 0 ) |
689 | 2.32k | return; |
690 | | |
691 | 1.26k | screen = &h->screen[Eia608GetWritingScreenIndex( h )]; |
692 | | |
693 | | /* FIXME do we need to reset row_used/colors/font ? */ |
694 | 1.26k | screen->characters[i_row][i_column] = ' '; |
695 | 1.26k | Eia608Cursor( h, -1 ); |
696 | 1.26k | } |
697 | | static void Eia608EraseToEndOfRow( eia608_t *h ) |
698 | 2.81k | { |
699 | 2.81k | if( h->mode == EIA608_MODE_TEXT ) |
700 | 192 | return; |
701 | | |
702 | 2.62k | Eia608ClearScreenRowX( h, Eia608GetWritingScreenIndex( h ), h->cursor.i_row, h->cursor.i_column ); |
703 | 2.62k | } |
704 | | |
705 | | static void Eia608RollUp( eia608_t *h ) |
706 | 3.15k | { |
707 | 3.15k | if( h->mode == EIA608_MODE_TEXT ) |
708 | 296 | return; |
709 | | |
710 | 2.86k | const int i_screen = Eia608GetWritingScreenIndex( h ); |
711 | 2.86k | eia608_screen *screen = &h->screen[i_screen]; |
712 | | |
713 | 2.86k | int keep_lines; |
714 | | |
715 | | /* Window size */ |
716 | 2.86k | if( h->mode == EIA608_MODE_ROLLUP_2 ) |
717 | 384 | keep_lines = 2; |
718 | 2.47k | else if( h->mode == EIA608_MODE_ROLLUP_3 ) |
719 | 1.50k | keep_lines = 3; |
720 | 972 | else if( h->mode == EIA608_MODE_ROLLUP_4 ) |
721 | 656 | keep_lines = 4; |
722 | 316 | else |
723 | 316 | return; |
724 | | |
725 | | /* Reset the cursor */ |
726 | 2.54k | h->cursor.i_column = 0; |
727 | | |
728 | | /* Erase lines above our window */ |
729 | 23.5k | for( int i = 0; i < h->cursor.i_row - keep_lines; i++ ) |
730 | 21.0k | Eia608ClearScreenRow( h, i_screen, i ); |
731 | | |
732 | | /* Move up */ |
733 | 7.91k | for( int i = 0; i < keep_lines-1; i++ ) |
734 | 5.36k | { |
735 | 5.36k | const int i_row = h->cursor.i_row - keep_lines + i + 1; |
736 | 5.36k | if( i_row < 0 ) |
737 | 1.12k | continue; |
738 | 5.36k | assert( i_row+1 < EIA608_SCREEN_ROWS ); |
739 | 4.23k | memcpy( screen->characters[i_row], screen->characters[i_row+1], sizeof(*screen->characters) ); |
740 | 4.23k | memcpy( screen->colors[i_row], screen->colors[i_row+1], sizeof(*screen->colors) ); |
741 | 4.23k | memcpy( screen->fonts[i_row], screen->fonts[i_row+1], sizeof(*screen->fonts) ); |
742 | 4.23k | screen->row_used[i_row] = screen->row_used[i_row+1]; |
743 | 4.23k | } |
744 | | /* Reset current row */ |
745 | 2.54k | Eia608ClearScreenRow( h, i_screen, h->cursor.i_row ); |
746 | 2.54k | } |
747 | | static void Eia608ParseChannel( eia608_t *h, const uint8_t d[2] ) |
748 | 99.0k | { |
749 | | /* Check odd parity */ |
750 | 99.0k | static const int p4[16] = { |
751 | 99.0k | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 |
752 | 99.0k | }; |
753 | 99.0k | if( p4[d[0] & 0xf] == p4[d[0] >> 4] || |
754 | 93.2k | p4[d[1] & 0xf] == p4[ d[1] >> 4] ) |
755 | 10.7k | { |
756 | 10.7k | h->i_channel = -1; |
757 | 10.7k | return; |
758 | 10.7k | } |
759 | | |
760 | | /* */ |
761 | 88.3k | const int d1 = d[0] & 0x7f; |
762 | 88.3k | if( d1 >= 0x10 && d1 <= 0x1f ) |
763 | 57.7k | h->i_channel = 1 + ((d1 & 0x08) != 0); |
764 | 30.5k | else if( d1 < 0x10 ) |
765 | 5.04k | h->i_channel = 3; |
766 | 88.3k | } |
767 | | static eia608_status_t Eia608ParseTextAttribute( eia608_t *h, uint8_t d2 ) |
768 | 845 | { |
769 | 845 | const int i_index = d2 - 0x20; |
770 | 845 | assert( d2 >= 0x20 && d2 <= 0x2f ); |
771 | | |
772 | 845 | Debug(printf("[TA %d]", i_index)); |
773 | 845 | h->color = pac2_attribs[i_index].i_color; |
774 | 845 | h->font = pac2_attribs[i_index].i_font; |
775 | 845 | Eia608Cursor( h, 1 ); |
776 | | |
777 | 845 | return EIA608_STATUS_DEFAULT; |
778 | 845 | } |
779 | | static eia608_status_t Eia608ParseSingle( eia608_t *h, const uint8_t dx ) |
780 | 20.0k | { |
781 | 20.0k | assert( dx >= 0x20 ); |
782 | 20.0k | Eia608Write( h, dx ); |
783 | 20.0k | return EIA608_STATUS_CHANGED; |
784 | 20.0k | } |
785 | | static eia608_status_t Eia608ParseDouble( eia608_t *h, uint8_t d2 ) |
786 | 2.91k | { |
787 | 2.91k | assert( d2 >= 0x30 && d2 <= 0x3f ); |
788 | 2.91k | Debug(printf("\033[0;33m%s\033[0m", d2 + 0x50)); |
789 | 2.91k | Eia608Write( h, d2 + 0x50 ); /* We use characters 0x80...0x8f */ |
790 | 2.91k | return EIA608_STATUS_CHANGED; |
791 | 2.91k | } |
792 | | static eia608_status_t Eia608ParseExtended( eia608_t *h, uint8_t d1, uint8_t d2 ) |
793 | 4.28k | { |
794 | 4.28k | assert( d2 >= 0x20 && d2 <= 0x3f ); |
795 | 4.28k | assert( d1 == 0x12 || d1 == 0x13 ); |
796 | 4.28k | if( d1 == 0x12 ) |
797 | 3.72k | d2 += 0x70; /* We use characters 0x90-0xaf */ |
798 | 562 | else |
799 | 562 | d2 += 0x90; /* We use characters 0xb0-0xcf */ |
800 | | |
801 | 4.28k | Debug(printf("[EXT %x->'%c']", d2, d2)); |
802 | | /* The extended characters replace the previous one with a more |
803 | | * advanced one */ |
804 | 4.28k | Eia608Cursor( h, -1 ); |
805 | 4.28k | Eia608Write( h, d2 ); |
806 | 4.28k | return EIA608_STATUS_CHANGED; |
807 | 4.28k | } |
808 | | static eia608_status_t Eia608ParseCommand0x14( eia608_t *h, uint8_t d2 ) |
809 | 18.5k | { |
810 | 18.5k | eia608_status_t i_status = EIA608_STATUS_DEFAULT; |
811 | 18.5k | eia608_mode_t proposed_mode; |
812 | | |
813 | 18.5k | switch( d2 ) |
814 | 18.5k | { |
815 | 680 | case 0x20: /* Resume caption loading */ |
816 | 680 | Debug(printf("[RCL]")); |
817 | 680 | h->mode = EIA608_MODE_POPUP; |
818 | 680 | break; |
819 | 3.75k | case 0x21: /* Backspace */ |
820 | 3.75k | Debug(printf("[BS]")); |
821 | 3.75k | Eia608Erase( h ); |
822 | 3.75k | i_status = EIA608_STATUS_CHANGED; |
823 | 3.75k | break; |
824 | 5 | case 0x22: /* Reserved */ |
825 | 6 | case 0x23: |
826 | 6 | Debug(printf("[ALARM %d]", d2 - 0x22)); |
827 | 6 | break; |
828 | 2.81k | case 0x24: /* Delete to end of row */ |
829 | 2.81k | Debug(printf("[DER]")); |
830 | 2.81k | Eia608EraseToEndOfRow( h ); |
831 | 2.81k | break; |
832 | 411 | case 0x25: /* Rollup 2 */ |
833 | 1.78k | case 0x26: /* Rollup 3 */ |
834 | 1.98k | case 0x27: /* Rollup 4 */ |
835 | 1.98k | Debug(printf("[RU%d]", d2 - 0x23)); |
836 | 1.98k | if( h->mode == EIA608_MODE_POPUP || h->mode == EIA608_MODE_PAINTON ) |
837 | 988 | { |
838 | 988 | Eia608EraseScreen( h, true ); |
839 | 988 | Eia608EraseScreen( h, false ); |
840 | 988 | i_status = EIA608_STATUS_CHANGED | EIA608_STATUS_CAPTION_CLEARED; |
841 | 988 | } |
842 | | |
843 | 1.98k | if( d2 == 0x25 ) |
844 | 411 | proposed_mode = EIA608_MODE_ROLLUP_2; |
845 | 1.57k | else if( d2 == 0x26 ) |
846 | 1.37k | proposed_mode = EIA608_MODE_ROLLUP_3; |
847 | 202 | else |
848 | 202 | proposed_mode = EIA608_MODE_ROLLUP_4; |
849 | | |
850 | 1.98k | if ( proposed_mode != h->mode ) |
851 | 1.04k | { |
852 | 1.04k | h->mode = proposed_mode; |
853 | 1.04k | h->cursor.i_column = 0; |
854 | 1.04k | h->cursor.i_row = h->i_row_rollup; |
855 | 1.04k | } |
856 | 1.98k | break; |
857 | 0 | case 0x28: /* Flash on */ |
858 | 0 | Debug(printf("[FON]")); |
859 | | /* TODO */ |
860 | 0 | break; |
861 | 2.78k | case 0x29: /* Resume direct captionning */ |
862 | 2.78k | Debug(printf("[RDC]")); |
863 | 2.78k | h->mode = EIA608_MODE_PAINTON; |
864 | 2.78k | break; |
865 | 154 | case 0x2a: /* Text restart */ |
866 | 154 | Debug(printf("[TR]")); |
867 | | /* TODO */ |
868 | 154 | break; |
869 | | |
870 | 254 | case 0x2b: /* Resume text display */ |
871 | 254 | Debug(printf("[RTD]")); |
872 | 254 | h->mode = EIA608_MODE_TEXT; |
873 | 254 | break; |
874 | | |
875 | 690 | case 0x2c: /* Erase displayed memory */ |
876 | 690 | Debug(printf("[EDM]")); |
877 | 690 | Eia608EraseScreen( h, true ); |
878 | 690 | i_status = EIA608_STATUS_CHANGED | EIA608_STATUS_CAPTION_CLEARED; |
879 | 690 | break; |
880 | 3.15k | case 0x2d: /* Carriage return */ |
881 | 3.15k | Debug(printf("[CR]")); |
882 | 3.15k | Eia608RollUp(h); |
883 | 3.15k | i_status = EIA608_STATUS_CHANGED; |
884 | 3.15k | break; |
885 | 960 | case 0x2e: /* Erase non displayed memory */ |
886 | 960 | Debug(printf("[ENM]")); |
887 | 960 | Eia608EraseScreen( h, false ); |
888 | 960 | break; |
889 | 1.32k | case 0x2f: /* End of caption (flip screen if not paint on) */ |
890 | 1.32k | Debug(printf("[EOC]")); |
891 | 1.32k | if( h->mode != EIA608_MODE_PAINTON ) |
892 | 383 | h->i_screen = 1 - h->i_screen; |
893 | 1.32k | h->mode = EIA608_MODE_POPUP; |
894 | 1.32k | h->cursor.i_column = 0; |
895 | 1.32k | h->cursor.i_row = 0; |
896 | 1.32k | h->color = EIA608_COLOR_DEFAULT; |
897 | 1.32k | h->font = EIA608_FONT_REGULAR; |
898 | 1.32k | i_status = EIA608_STATUS_CHANGED | EIA608_STATUS_CAPTION_ENDED; |
899 | 1.32k | break; |
900 | 18.5k | } |
901 | 18.5k | return i_status; |
902 | 18.5k | } |
903 | | static bool Eia608ParseCommand0x17( eia608_t *h, uint8_t d2 ) |
904 | 754 | { |
905 | 754 | switch( d2 ) |
906 | 754 | { |
907 | 153 | case 0x21: /* Tab offset 1 */ |
908 | 754 | case 0x22: /* Tab offset 2 */ |
909 | 754 | case 0x23: /* Tab offset 3 */ |
910 | 754 | Debug(printf("[TO%d]", d2 - 0x20)); |
911 | 754 | Eia608Cursor( h, d2 - 0x20 ); |
912 | 754 | break; |
913 | 754 | } |
914 | 754 | return false; |
915 | 754 | } |
916 | | static bool Eia608ParsePac( eia608_t *h, uint8_t d1, uint8_t d2 ) |
917 | 4.06k | { |
918 | 4.06k | static const int pi_row[] = { |
919 | 4.06k | 11, -1, 1, 2, 3, 4, 12, 13, 14, 15, 5, 6, 7, 8, 9, 10 |
920 | 4.06k | }; |
921 | 4.06k | const int i_row_index = ( (d1<<1) & 0x0e) | ( (d2>>5) & 0x01 ); |
922 | | |
923 | 4.06k | Debug(printf("[PAC,%d]", i_row_index)); |
924 | 4.06k | assert( d2 >= 0x40 && d2 <= 0x7f ); |
925 | | |
926 | 4.06k | if( pi_row[i_row_index] <= 0 ) |
927 | 0 | return false; |
928 | | |
929 | | /* Row */ |
930 | 4.06k | if( h->mode != EIA608_MODE_TEXT ) |
931 | 3.41k | h->cursor.i_row = pi_row[i_row_index] - 1; |
932 | 4.06k | h->i_row_rollup = pi_row[i_row_index] - 1; |
933 | | /* Column */ |
934 | 4.06k | if( d2 >= 0x60 ) |
935 | 2.44k | d2 -= 0x60; |
936 | 1.61k | else if( d2 >= 0x40 ) |
937 | 1.61k | d2 -= 0x40; |
938 | 4.06k | h->cursor.i_column = pac2_attribs[d2].i_column; |
939 | 4.06k | h->color = pac2_attribs[d2].i_color; |
940 | 4.06k | h->font = pac2_attribs[d2].i_font; |
941 | | |
942 | 4.06k | return false; |
943 | 4.06k | } |
944 | | |
945 | | static eia608_status_t Eia608ParseData( eia608_t *h, uint8_t d1, uint8_t d2 ) |
946 | 46.5k | { |
947 | 46.5k | eia608_status_t i_status = EIA608_STATUS_DEFAULT; |
948 | | |
949 | 46.5k | if( d1 >= 0x18 && d1 <= 0x1f ) |
950 | 10 | d1 -= 8; |
951 | | |
952 | 77.1k | #define ON( d2min, d2max, cmd ) do { if( d2 >= d2min && d2 <= d2max ) i_status = cmd; } while(0) |
953 | 46.5k | switch( d1 ) |
954 | 46.5k | { |
955 | 4.66k | case 0x11: |
956 | 4.66k | ON( 0x20, 0x2f, Eia608ParseTextAttribute( h, d2 ) ); |
957 | 4.66k | ON( 0x30, 0x3f, Eia608ParseDouble( h, d2 ) ); |
958 | 4.66k | break; |
959 | 4.98k | case 0x12: case 0x13: |
960 | 4.98k | ON( 0x20, 0x3f, Eia608ParseExtended( h, d1, d2 ) ); |
961 | 4.98k | break; |
962 | 22.9k | case 0x14: case 0x15: |
963 | 22.9k | ON( 0x20, 0x2f, Eia608ParseCommand0x14( h, d2 ) ); |
964 | 22.9k | break; |
965 | 2.20k | case 0x17: |
966 | 2.20k | ON( 0x21, 0x23, Eia608ParseCommand0x17( h, d2 ) ); |
967 | 2.20k | ON( 0x2e, 0x2f, Eia608ParseTextAttribute( h, d2 ) ); |
968 | 2.20k | break; |
969 | 46.5k | } |
970 | 46.5k | if( d1 == 0x10 ) |
971 | 584 | ON( 0x40, 0x5f, Eia608ParsePac( h, d1, d2 ) ); |
972 | 45.9k | else if( d1 >= 0x11 && d1 <= 0x17 ) |
973 | 34.8k | ON( 0x40, 0x7f, Eia608ParsePac( h, d1, d2 ) ); |
974 | 46.5k | #undef ON |
975 | 46.5k | if( d1 >= 0x20 ) |
976 | 11.1k | { |
977 | 11.1k | Debug(printf("\033[0;33m%c", d1)); |
978 | 11.1k | i_status = Eia608ParseSingle( h, d1 ); |
979 | 11.1k | if( d2 >= 0x20 ) |
980 | 8.92k | { |
981 | 8.92k | Debug(printf("%c", d2)); |
982 | 8.92k | i_status |= Eia608ParseSingle( h, d2 ); |
983 | 8.92k | } |
984 | 11.1k | Debug(printf("\033[0m")); |
985 | 11.1k | } |
986 | | |
987 | | /* Ignore changes occurring to doublebuffer */ |
988 | 46.5k | if( h->mode == EIA608_MODE_POPUP && i_status == EIA608_STATUS_CHANGED ) |
989 | 5.47k | i_status = EIA608_STATUS_DEFAULT; |
990 | | |
991 | 46.5k | return i_status; |
992 | 46.5k | } |
993 | | |
994 | | static void Eia608TextUtf8( char *psz_utf8, uint8_t c ) // Returns number of bytes used |
995 | 347k | { |
996 | 4.52M | #define E1(c,u) { c, { u, '\0' } } |
997 | 23.6M | #define E2(c,u1,u2) { c, { u1, u2, '\0' } } |
998 | 3.13M | #define E3(c,u1,u2,u3) { c, { u1, u2, u3, '\0' } } |
999 | 347k | static const struct { |
1000 | 347k | uint8_t c; |
1001 | 347k | char utf8[3+1]; |
1002 | 347k | } c2utf8[] = { |
1003 | | // Regular line-21 character set, mostly ASCII except these exceptions |
1004 | 347k | E2( 0x2a, 0xc3,0xa1), // lowercase a, acute accent |
1005 | 347k | E2( 0x5c, 0xc3,0xa9), // lowercase e, acute accent |
1006 | 347k | E2( 0x5e, 0xc3,0xad), // lowercase i, acute accent |
1007 | 347k | E2( 0x5f, 0xc3,0xb3), // lowercase o, acute accent |
1008 | 347k | E2( 0x60, 0xc3,0xba), // lowercase u, acute accent |
1009 | 347k | E2( 0x7b, 0xc3,0xa7), // lowercase c with cedilla |
1010 | 347k | E2( 0x7c, 0xc3,0xb7), // division symbol |
1011 | 347k | E2( 0x7d, 0xc3,0x91), // uppercase N tilde |
1012 | 347k | 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 | 347k | E2( 0x80, 0xc2,0xae), // Registered symbol (R) |
1016 | 347k | E2( 0x81, 0xc2,0xb0), // degree sign |
1017 | 347k | E2( 0x82, 0xc2,0xbd), // 1/2 symbol |
1018 | 347k | E2( 0x83, 0xc2,0xbf), // Inverted (open) question mark |
1019 | 347k | E3( 0x84, 0xe2,0x84,0xa2), // Trademark symbol (TM) |
1020 | 347k | E2( 0x85, 0xc2,0xa2), // Cents symbol |
1021 | 347k | E2( 0x86, 0xc2,0xa3), // Pounds sterling |
1022 | 347k | E3( 0x87, 0xe2,0x99,0xaa), // Music note |
1023 | 347k | E2( 0x88, 0xc3,0xa0), // lowercase a, grave accent |
1024 | 347k | E2( 0x89, 0xc2,0xa0), // transparent space |
1025 | 347k | E2( 0x8a, 0xc3,0xa8), // lowercase e, grave accent |
1026 | 347k | E2( 0x8b, 0xc3,0xa2), // lowercase a, circumflex accent |
1027 | 347k | E2( 0x8c, 0xc3,0xaa), // lowercase e, circumflex accent |
1028 | 347k | E2( 0x8d, 0xc3,0xae), // lowercase i, circumflex accent |
1029 | 347k | E2( 0x8e, 0xc3,0xb4), // lowercase o, circumflex accent |
1030 | 347k | 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 | 347k | E2( 0x90, 0xc3,0x81), // capital letter A with acute |
1034 | 347k | E2( 0x91, 0xc3,0x89), // capital letter E with acute |
1035 | 347k | E2( 0x92, 0xc3,0x93), // capital letter O with acute |
1036 | 347k | E2( 0x93, 0xc3,0x9a), // capital letter U with acute |
1037 | 347k | E2( 0x94, 0xc3,0x9c), // capital letter U with diaeresis |
1038 | 347k | E2( 0x95, 0xc3,0xbc), // lowercase letter U with diaeresis |
1039 | 347k | E1( 0x96, 0x27), // apostrophe |
1040 | 347k | E2( 0x97, 0xc2,0xa1), // inverted exclamation mark |
1041 | 347k | E1( 0x98, 0x2a), // asterisk |
1042 | 347k | E1( 0x99, 0x27), // apostrophe (yes, duped). See CCADI source code. |
1043 | 347k | E1( 0x9a, 0x2d), // hyphen-minus |
1044 | 347k | E2( 0x9b, 0xc2,0xa9), // copyright sign |
1045 | 347k | E3( 0x9c, 0xe2,0x84,0xa0), // Service mark |
1046 | 347k | E1( 0x9d, 0x2e), // Full stop (.) |
1047 | 347k | E3( 0x9e, 0xe2,0x80,0x9c), // Quotation mark |
1048 | 347k | E3( 0x9f, 0xe2,0x80,0x9d), // Quotation mark |
1049 | 347k | E2( 0xa0, 0xc3,0x80), // uppercase A, grave accent |
1050 | 347k | E2( 0xa1, 0xc3,0x82), // uppercase A, circumflex |
1051 | 347k | E2( 0xa2, 0xc3,0x87), // uppercase C with cedilla |
1052 | 347k | E2( 0xa3, 0xc3,0x88), // uppercase E, grave accent |
1053 | 347k | E2( 0xa4, 0xc3,0x8a), // uppercase E, circumflex |
1054 | 347k | E2( 0xa5, 0xc3,0x8b), // capital letter E with diaeresis |
1055 | 347k | E2( 0xa6, 0xc3,0xab), // lowercase letter e with diaeresis |
1056 | 347k | E2( 0xa7, 0xc3,0x8e), // uppercase I, circumflex |
1057 | 347k | E2( 0xa8, 0xc3,0x8f), // uppercase I, with diaeresis |
1058 | 347k | E2( 0xa9, 0xc3,0xaf), // lowercase i, with diaeresis |
1059 | 347k | E2( 0xaa, 0xc3,0x94), // uppercase O, circumflex |
1060 | 347k | E2( 0xab, 0xc3,0x99), // uppercase U, grave accent |
1061 | 347k | E2( 0xac, 0xc3,0xb9), // lowercase u, grave accent |
1062 | 347k | E2( 0xad, 0xc3,0x9b), // uppercase U, circumflex |
1063 | 347k | E2( 0xae, 0xc2,0xab), // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK |
1064 | 347k | 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 | 347k | E2( 0xb0, 0xc3,0x83), // Uppercase A, tilde |
1068 | 347k | E2( 0xb1, 0xc3,0xa3), // Lowercase a, tilde |
1069 | 347k | E2( 0xb2, 0xc3,0x8d), // Uppercase I, acute accent |
1070 | 347k | E2( 0xb3, 0xc3,0x8c), // Uppercase I, grave accent |
1071 | 347k | E2( 0xb4, 0xc3,0xac), // Lowercase i, grave accent |
1072 | 347k | E2( 0xb5, 0xc3,0x92), // Uppercase O, grave accent |
1073 | 347k | E2( 0xb6, 0xc3,0xb2), // Lowercase o, grave accent |
1074 | 347k | E2( 0xb7, 0xc3,0x95), // Uppercase O, tilde |
1075 | 347k | E2( 0xb8, 0xc3,0xb5), // Lowercase o, tilde |
1076 | 347k | E1( 0xb9, 0x7b), // Open curly brace |
1077 | 347k | E1( 0xba, 0x7d), // Closing curly brace |
1078 | 347k | E1( 0xbb, 0x5c), // Backslash |
1079 | 347k | E1( 0xbc, 0x5e), // Caret |
1080 | 347k | E1( 0xbd, 0x5f), // Underscore |
1081 | 347k | E2( 0xbe, 0xc2,0xa6), // Pipe (broken bar) |
1082 | 347k | E1( 0xbf, 0x7e), // Tilde (utf8 code unsure) |
1083 | 347k | E2( 0xc0, 0xc3,0x84), // Uppercase A, umlaut |
1084 | 347k | E2( 0xc1, 0xc3,0xa4), // Lowercase A, umlaut |
1085 | 347k | E2( 0xc2, 0xc3,0x96), // Uppercase O, umlaut |
1086 | 347k | E2( 0xc3, 0xc3,0xb6), // Lowercase o, umlaut |
1087 | 347k | E2( 0xc4, 0xc3,0x9f), // Esszett (sharp S) |
1088 | 347k | E2( 0xc5, 0xc2,0xa5), // Yen symbol |
1089 | 347k | E2( 0xc6, 0xc2,0xa4), // Currency symbol |
1090 | 347k | E1( 0xc7, 0x7c), // Vertical bar |
1091 | 347k | E2( 0xc8, 0xc3,0x85), // Uppercase A, ring |
1092 | 347k | E2( 0xc9, 0xc3,0xa5), // Lowercase A, ring |
1093 | 347k | E2( 0xca, 0xc3,0x98), // Uppercase O, slash |
1094 | 347k | E2( 0xcb, 0xc3,0xb8), // Lowercase o, slash |
1095 | 347k | E3( 0xcc, 0xe2,0x8c,0x9c), // Upper left corner |
1096 | 347k | E3( 0xcd, 0xe2,0x8c,0x9d), // Upper right corner |
1097 | 347k | E3( 0xce, 0xe2,0x8c,0x9e), // Lower left corner |
1098 | 347k | E3( 0xcf, 0xe2,0x8c,0x9f), // Lower right corner |
1099 | | |
1100 | 347k | E1(0,0) |
1101 | 347k | }; |
1102 | 347k | #undef E3 |
1103 | 347k | #undef E2 |
1104 | 347k | #undef E1 |
1105 | | |
1106 | 17.3M | for( size_t i = 0; i < ARRAY_SIZE(c2utf8) ; i++ ) |
1107 | 17.2M | if( c2utf8[i].c == c ) { |
1108 | 203k | strcpy( psz_utf8, c2utf8[i].utf8 ); |
1109 | 203k | return; |
1110 | 203k | } |
1111 | | |
1112 | 144k | psz_utf8[0] = c < 0x80 ? c : '?'; /* Normal : Unsupported */ |
1113 | 144k | psz_utf8[1] = '\0'; |
1114 | 144k | } |
1115 | | |
1116 | | static void Eia608Strlcat( char *d, const char *s, int i_max ) |
1117 | 347k | { |
1118 | 347k | if( i_max > 1 ) |
1119 | 347k | strncat( d, s, i_max-1 - strnlen(d, i_max-1)); |
1120 | 347k | if( i_max > 0 ) |
1121 | 347k | d[i_max-1] = '\0'; |
1122 | 347k | } |
1123 | | |
1124 | 347k | #define CAT(t) Eia608Strlcat( psz_text, t, ARRAY_SIZE(psz_text) ) |
1125 | | |
1126 | | static text_segment_t * Eia608TextLine( struct eia608_screen *screen, int i_row ) |
1127 | 29.7k | { |
1128 | 29.7k | const uint8_t *p_char = screen->characters[i_row]; |
1129 | 29.7k | const eia608_color_t *p_color = screen->colors[i_row]; |
1130 | 29.7k | const eia608_font_t *p_font = screen->fonts[i_row]; |
1131 | 29.7k | int i_start; |
1132 | 29.7k | int i_end; |
1133 | 29.7k | int x; |
1134 | 29.7k | eia608_color_t prev_color = EIA608_COLOR_DEFAULT; |
1135 | 29.7k | eia608_font_t prev_font = EIA608_FONT_REGULAR; |
1136 | | |
1137 | 29.7k | char utf8[4]; |
1138 | 29.7k | char psz_text[(4 * EIA608_SCREEN_COLUMNS + 1) + 1]; |
1139 | 29.7k | psz_text[0] = '\0'; |
1140 | | |
1141 | | /* Search the start */ |
1142 | 29.7k | i_start = 0; |
1143 | | |
1144 | | /* Convert leading spaces to non-breaking so that they don't get |
1145 | | stripped by the RenderHtml routine as regular whitespace */ |
1146 | 189k | while( i_start < EIA608_SCREEN_COLUMNS && p_char[i_start] == ' ' ) { |
1147 | 159k | Eia608TextUtf8( utf8, 0x89 ); |
1148 | 159k | CAT( utf8 ); |
1149 | 159k | i_start++; |
1150 | 159k | } |
1151 | | |
1152 | | /* Search the end */ |
1153 | 29.7k | i_end = EIA608_SCREEN_COLUMNS-1; |
1154 | 632k | while( i_end > i_start && p_char[i_end] == ' ' ) |
1155 | 602k | i_end--; |
1156 | | |
1157 | | /* */ |
1158 | 29.7k | if( i_start > i_end ) /* Nothing to render */ |
1159 | 3.70k | return NULL; |
1160 | | |
1161 | 25.9k | text_segment_t *p_segment, *p_segments_head = p_segment = text_segment_New( NULL ); |
1162 | 25.9k | if(!p_segment) |
1163 | 0 | return NULL; |
1164 | | |
1165 | 25.9k | p_segment->style = text_style_Create( STYLE_NO_DEFAULTS ); |
1166 | 25.9k | if(!p_segment->style) |
1167 | 0 | { |
1168 | 0 | text_segment_Delete(p_segment); |
1169 | 0 | return NULL; |
1170 | 0 | } |
1171 | | /* Ensure we get a monospaced font (required for accurate positioning */ |
1172 | 25.9k | p_segment->style->i_style_flags |= STYLE_MONOSPACED; |
1173 | | |
1174 | 213k | for( x = i_start; x <= i_end; x++ ) |
1175 | 187k | { |
1176 | 187k | eia608_color_t color = p_color[x]; |
1177 | 187k | eia608_font_t font = p_font[x]; |
1178 | | |
1179 | 187k | if(font != prev_font || color != prev_color) |
1180 | 17.5k | { |
1181 | 17.5k | EnsureUTF8(psz_text); |
1182 | 17.5k | p_segment->psz_text = strdup(psz_text); |
1183 | 17.5k | psz_text[0] = '\0'; |
1184 | 17.5k | p_segment->p_next = text_segment_New( NULL ); |
1185 | 17.5k | p_segment = p_segment->p_next; |
1186 | 17.5k | if(!p_segment) |
1187 | 0 | return p_segments_head; |
1188 | | |
1189 | 17.5k | p_segment->style = text_style_Create( STYLE_NO_DEFAULTS ); |
1190 | 17.5k | if(!p_segment->style) |
1191 | 0 | { |
1192 | 0 | text_segment_Delete(p_segment); |
1193 | 0 | return p_segments_head; |
1194 | 0 | } |
1195 | 17.5k | p_segment->style->i_style_flags |= STYLE_MONOSPACED; |
1196 | | |
1197 | | /* start segment with new style */ |
1198 | 17.5k | if(font & EIA608_FONT_ITALICS) |
1199 | 7.13k | { |
1200 | 7.13k | p_segment->style->i_style_flags |= STYLE_ITALIC; |
1201 | 7.13k | p_segment->style->i_features |= STYLE_HAS_FLAGS; |
1202 | 7.13k | } |
1203 | 17.5k | if(font & EIA608_FONT_UNDERLINE) |
1204 | 4.11k | { |
1205 | 4.11k | p_segment->style->i_style_flags |= STYLE_UNDERLINE; |
1206 | 4.11k | p_segment->style->i_features |= STYLE_HAS_FLAGS; |
1207 | 4.11k | } |
1208 | | |
1209 | 17.5k | if(color != EIA608_COLOR_DEFAULT) |
1210 | 5.28k | { |
1211 | 5.28k | p_segment->style->i_font_color = rgi_eia608_colors[color]; |
1212 | 5.28k | p_segment->style->i_features |= STYLE_HAS_FONT_COLOR; |
1213 | 5.28k | } |
1214 | 17.5k | } |
1215 | | |
1216 | 187k | Eia608TextUtf8( utf8, p_char[x] ); |
1217 | 187k | CAT( utf8 ); |
1218 | | |
1219 | | /* */ |
1220 | 187k | prev_font = font; |
1221 | 187k | prev_color = color; |
1222 | 187k | } |
1223 | | |
1224 | 25.9k | #undef CAT |
1225 | | |
1226 | 25.9k | if( p_segment ) |
1227 | 25.9k | { |
1228 | 25.9k | assert(!p_segment->psz_text); // shouldn't happen |
1229 | 25.9k | EnsureUTF8(psz_text); |
1230 | 25.9k | p_segment->psz_text = strdup(psz_text); |
1231 | 25.9k | } |
1232 | | |
1233 | 25.9k | return p_segments_head; |
1234 | 25.9k | } |
1235 | | |
1236 | | static void Eia608FillUpdaterRegions( subtext_updater_sys_t *p_updater, eia608_t *h ) |
1237 | 22.7k | { |
1238 | 22.7k | struct eia608_screen *screen = &h->screen[h->i_screen]; |
1239 | 22.7k | substext_updater_region_t *p_region = &p_updater->region; |
1240 | 22.7k | text_segment_t **pp_last = &p_region->p_segments; |
1241 | 22.7k | bool b_newregion = false; |
1242 | | |
1243 | 364k | for( int i = 0; i < EIA608_SCREEN_ROWS; i++ ) |
1244 | 341k | { |
1245 | 341k | if( !screen->row_used[i] ) |
1246 | 312k | continue; |
1247 | | |
1248 | 29.7k | text_segment_t *p_segments = Eia608TextLine( screen, i ); |
1249 | 29.7k | if( p_segments ) |
1250 | 25.9k | { |
1251 | 25.9k | if( b_newregion ) |
1252 | 418 | { |
1253 | 418 | substext_updater_region_t *p_newregion; |
1254 | 418 | p_newregion = SubpictureUpdaterSysRegionNew(); |
1255 | 418 | if( !p_newregion ) |
1256 | 0 | { |
1257 | 0 | text_segment_ChainDelete( p_segments ); |
1258 | 0 | return; |
1259 | 0 | } |
1260 | | /* Copy defaults */ |
1261 | 418 | p_newregion->align = p_region->align; |
1262 | 418 | p_newregion->inner_align = p_region->inner_align; |
1263 | 418 | p_newregion->flags = p_region->flags; |
1264 | 418 | SubpictureUpdaterSysRegionAdd( p_region, p_newregion ); |
1265 | 418 | p_region = p_newregion; |
1266 | 418 | pp_last = &p_region->p_segments; |
1267 | 418 | b_newregion = false; |
1268 | 418 | } |
1269 | | |
1270 | 25.9k | if( p_region->p_segments == NULL ) /* First segment in the [new] region */ |
1271 | 14.3k | { |
1272 | 14.3k | p_region->origin.y = (float) i /* start line number */ |
1273 | 14.3k | / (EIA608_SCREEN_ROWS * FONT_TO_LINE_HEIGHT_RATIO); |
1274 | 14.3k | p_region->flags |= UPDT_REGION_ORIGIN_Y_IS_RATIO; |
1275 | 14.3k | } |
1276 | 11.6k | else /* Insert line break between region lines */ |
1277 | 11.6k | { |
1278 | 11.6k | *pp_last = text_segment_New( "\n" ); |
1279 | 11.6k | if( *pp_last ) |
1280 | 11.6k | pp_last = &((*pp_last)->p_next); |
1281 | 11.6k | } |
1282 | | |
1283 | 25.9k | *pp_last = p_segments; |
1284 | 43.5k | do { pp_last = &((*pp_last)->p_next); } while ( *pp_last != NULL ); |
1285 | 25.9k | } |
1286 | 3.70k | else |
1287 | 3.70k | { |
1288 | 3.70k | b_newregion = !!p_region->p_segments; |
1289 | 3.70k | } |
1290 | 29.7k | } |
1291 | 22.7k | } |
1292 | | |
1293 | | /* */ |
1294 | | static void Eia608Init( eia608_t *h ) |
1295 | 2.42k | { |
1296 | 2.42k | memset( h, 0, sizeof(*h) ); |
1297 | | |
1298 | | /* */ |
1299 | 2.42k | h->i_channel = -1; |
1300 | | |
1301 | 2.42k | h->i_screen = 0; |
1302 | 2.42k | Eia608ClearScreen( h, 0 ); |
1303 | 2.42k | Eia608ClearScreen( h, 1 ); |
1304 | | |
1305 | | /* Cursor for writing text */ |
1306 | 2.42k | h->cursor.i_column = 0; |
1307 | 2.42k | h->cursor.i_row = 0; |
1308 | | |
1309 | 2.42k | h->last.d1 = 0x00; |
1310 | 2.42k | h->last.d2 = 0x00; |
1311 | 2.42k | h->mode = EIA608_MODE_POPUP; |
1312 | 2.42k | h->color = EIA608_COLOR_DEFAULT; |
1313 | 2.42k | h->font = EIA608_FONT_REGULAR; |
1314 | 2.42k | h->i_row_rollup = EIA608_SCREEN_ROWS-1; |
1315 | 2.42k | } |
1316 | | static eia608_status_t Eia608Parse( eia608_t *h, int i_channel_selected, const uint8_t data[2] ) |
1317 | 103k | { |
1318 | 103k | const uint8_t d1 = data[0] & 0x7f; /* Removed parity bit */ |
1319 | 103k | const uint8_t d2 = data[1] & 0x7f; |
1320 | 103k | eia608_status_t i_screen_status = EIA608_STATUS_DEFAULT; |
1321 | | |
1322 | 103k | if( d1 == 0 && d2 == 0 ) |
1323 | 3.99k | return EIA608_STATUS_DEFAULT; /* Ignore padding (parity check are sometimes invalid on them) */ |
1324 | | |
1325 | 99.0k | Eia608ParseChannel( h, data ); |
1326 | 99.0k | if( h->i_channel != i_channel_selected ) |
1327 | 33.2k | return false; |
1328 | | //fprintf( stderr, "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC %x %x\n", data[0], data[1] ); |
1329 | | |
1330 | 65.8k | if( d1 >= 0x10 ) |
1331 | 65.8k | { |
1332 | 65.8k | if( d1 >= 0x20 || |
1333 | 54.6k | d1 != h->last.d1 || d2 != h->last.d2 ) /* Command codes can be repeated */ |
1334 | 46.5k | i_screen_status = Eia608ParseData( h, d1,d2 ); |
1335 | | |
1336 | 65.8k | h->last.d1 = d1; |
1337 | 65.8k | h->last.d2 = d2; |
1338 | 65.8k | } |
1339 | 0 | else if( ( d1 >= 0x01 && d1 <= 0x0E ) || d1 == 0x0F ) |
1340 | 0 | { |
1341 | | /* XDS block / End of XDS block */ |
1342 | 0 | } |
1343 | 65.8k | return i_screen_status; |
1344 | 99.0k | } |