summaryrefslogtreecommitdiff
path: root/src/dtvcc.c (plain)
blob: bb122555fee027b009da89d9cfbdc83616fa3e9a
1/*
2 * atsc-cc -- ATSC Closed Caption decoder
3 *
4 * Copyright (C) 2008 Michael H. Schimek <mschimek@users.sf.net>
5 *
6 * Contains code from zvbi-ntsc-cc closed caption decoder written by
7 * <timecop@japan.co.jp>, Mike Baker <mbm@linux.com>,
8 * Mark K. Kim <dev@cbreak.org>.
9 *
10 * Thanks to Karol Zapolski for his support.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 * MA 02110-1301, USA.
26 */
27
28#ifdef HAVE_CONFIG_H
29# include "config.h"
30#endif
31
32#define _GNU_SOURCE 1
33
34#include "libzvbi.h"
35#include "dtvcc.h"
36#include <android/log.h>
37
38#define elements(array) (sizeof(array) / sizeof(array[0]))
39
40#define LOG_TAG "ZVBI"
41#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
42#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
43
44#if __GNUC__ < 3
45# define likely(expr) (expr)
46# define unlikely(expr) (expr)
47#else
48# define likely(expr) __builtin_expect(expr, 1)
49# define unlikely(expr) __builtin_expect(expr, 0)
50#endif
51
52#define N_ELEMENTS(array) (sizeof (array) / sizeof ((array)[0]))
53#define CLEAR(var) memset (&(var), 0, sizeof (var))
54
55/* FIXME __typeof__ is a GCC extension. */
56#undef SWAP
57#define SWAP(x, y) \
58do { \
59 __typeof__ (x) _x = x; \
60 x = y; \
61 y = _x; \
62} while (0)
63
64#undef MIN
65#define MIN(x, y) ({ \
66 __typeof__ (x) _x = (x); \
67 __typeof__ (y) _y = (y); \
68 (void)(&_x == &_y); /* warn if types do not match */ \
69 /* return */ (_x < _y) ? _x : _y; \
70})
71
72#undef MAX
73#define MAX(x, y) ({ \
74 __typeof__ (x) _x = (x); \
75 __typeof__ (y) _y = (y); \
76 (void)(&_x == &_y); /* warn if types do not match */ \
77 /* return */ (_x > _y) ? _x : _y; \
78})
79
80#undef PARENT
81#define PARENT(_ptr, _type, _member) ({ \
82 __typeof__ (&((_type *) 0)->_member) _p = (_ptr); \
83 (_p != 0) ? (_type *)(((char *) _p) - offsetof (_type, \
84 _member)) : (_type *) 0; \
85})
86
87/* These should be defined in inttypes.h. */
88#ifndef PRId64
89# define PRId64 "lld"
90#endif
91#ifndef PRIu64
92# define PRIu64 "llu"
93#endif
94#ifndef PRIx64
95# define PRIx64 "llx"
96#endif
97
98extern void
99vbi_transp_colormap(vbi_decoder *vbi, vbi_rgba *d, vbi_rgba *s, int entries);
100extern void
101vbi_send_event(vbi_decoder *vbi, vbi_event *ev);
102extern void
103vbi_decode_caption(vbi_decoder *vbi, int line, uint8_t *buf);
104
105/* EIA 608-B decoder. */
106
107static int
108printable (int c)
109{
110 if ((c & 0x7F) < 0x20)
111 return '.';
112 else
113 return c & 0x7F;
114}
115
116static void
117dump (FILE * fp,
118 const uint8_t * buf,
119 unsigned int n_bytes)
120 _vbi_unused;
121
122static void
123dump (FILE * fp,
124 const uint8_t * buf,
125 unsigned int n_bytes)
126{
127 const unsigned int width = 16;
128 unsigned int i;
129
130 for (i = 0; i < n_bytes; i += width) {
131 unsigned int end;
132 unsigned int j;
133
134 end = MIN (i + width, n_bytes);
135 for (j = i; j < end; ++j)
136 fprintf (fp, "%02x ", buf[j]);
137 for (; j < i + width; ++j)
138 fputs (" ", fp);
139 fputc (' ', fp);
140 for (j = i; j < end; ++j) {
141 int c = buf[j];
142 fputc (printable (c), fp);
143 }
144 fputc ('\n', fp);
145 }
146}
147
148#define log(verb, templ, args...) \
149 log_message (2, /* print_errno */ FALSE, templ , ##args)
150
151#define log_errno(verb, templ, args...) \
152 log_message (2, /* print_errno */ TRUE, templ , ##args)
153
154#define bug(templ, args...) \
155 log_message (1, /* print_errno */ FALSE, "BUG: " templ , ##args)
156
157static void
158log_message (unsigned int verbosity,
159 vbi_bool print_errno,
160 const char * templ,
161 ...)
162{
163 if (1) {
164 va_list ap;
165
166 va_start (ap, templ);
167
168 fprintf (stderr, "TVCC: ");
169 vfprintf (stderr, templ, ap);
170
171 if (print_errno) {
172 fprintf (stderr, ": %s.\n",
173 strerror (errno));
174 }
175
176 va_end (ap);
177 }
178}
179
180static void *
181xmalloc (size_t size)
182{
183 void *p;
184
185 p = malloc (size);
186 if (NULL == p) {
187 log(2, "no memory");
188 }
189
190 return p;
191}
192
193static char *
194xasprintf (const char * templ,
195 ...)
196{
197 va_list ap;
198 char *s;
199 int r;
200
201 va_start (ap, templ);
202
203 r = vasprintf (&s, templ, ap);
204 if (r < 0 || NULL == s) {
205 return NULL;
206 }
207
208 va_end (ap);
209
210 return s;
211}
212
213_vbi_inline void
214vbi_char_copy_attr (struct vbi_char * cp1,
215 struct vbi_char * cp2,
216 unsigned int attr)
217{
218 if (attr & VBI_UNDERLINE)
219 cp1->underline = cp2->underline;
220 if (attr & VBI_ITALIC)
221 cp1->italic = cp2->italic;
222 if (attr & VBI_FLASH)
223 cp1->flash = cp2->flash;
224}
225
226_vbi_inline void
227vbi_char_clear_attr (struct vbi_char * cp,
228 unsigned int attr)
229{
230 if (attr & VBI_UNDERLINE)
231 cp->underline = 0;
232 if (attr & VBI_ITALIC)
233 cp->italic = 0;
234 if (attr & VBI_FLASH)
235 cp->flash = 0;
236}
237
238_vbi_inline void
239vbi_char_set_attr (struct vbi_char * cp,
240 unsigned int attr)
241{
242 if (attr & VBI_UNDERLINE)
243 cp->underline = 1;
244 if (attr & VBI_ITALIC)
245 cp->italic = 1;
246 if (attr & VBI_FLASH)
247 cp->flash = 1;
248}
249
250_vbi_inline unsigned int
251vbi_char_has_attr (struct vbi_char * cp,
252 unsigned int attr)
253{
254 attr &= (VBI_UNDERLINE | VBI_ITALIC | VBI_FLASH);
255
256 if (0 == cp->underline)
257 attr &= ~VBI_UNDERLINE;
258 if (0 == cp->italic)
259 attr &= ~VBI_ITALIC;
260 if (0 == cp->flash)
261 attr &= ~VBI_FLASH;
262
263 return attr;
264}
265
266_vbi_inline unsigned int
267vbi_char_xor_attr (struct vbi_char * cp1,
268 struct vbi_char * cp2,
269 unsigned int attr)
270{
271 attr &= (VBI_UNDERLINE | VBI_ITALIC | VBI_FLASH);
272
273 if (0 == (cp1->underline ^ cp2->underline))
274 attr &= ~VBI_UNDERLINE;
275 if (0 == (cp1->italic ^ cp2->italic))
276 attr &= ~VBI_ITALIC;
277 if (0 == (cp1->flash ^ cp2->flash))
278 attr &= ~VBI_FLASH;
279
280 return attr;
281}
282
283/* EIA 608-B Closed Caption decoder. */
284
285static void
286cc_timestamp_reset (struct cc_timestamp * ts)
287{
288 CLEAR (ts->sys);
289 ts->pts = -1;
290}
291
292static vbi_bool
293cc_timestamp_isset (struct cc_timestamp * ts)
294{
295 return (ts->pts >= 0 || 0 != (ts->sys.tv_sec | ts->sys.tv_usec));
296}
297
298static const vbi_color
299cc_color_map [8] = {
300 VBI_WHITE, VBI_GREEN, VBI_BLUE, VBI_CYAN,
301 VBI_RED, VBI_YELLOW, VBI_MAGENTA, VBI_BLACK
302};
303
304static const int8_t
305cc_pac_row_map [16] = {
306 /* 0 */ 10, /* 0x1040 */
307 /* 1 */ -1, /* no function */
308 /* 2 */ 0, 1, 2, 3, /* 0x1140 ... 0x1260 */
309 /* 6 */ 11, 12, 13, 14, /* 0x1340 ... 0x1460 */
310 /* 10 */ 4, 5, 6, 7, 8, 9 /* 0x1540 ... 0x1760 */
311};
312
313static vbi_pgno
314cc_channel_num (struct cc_decoder * cd,
315 struct cc_channel * ch)
316{
317 return (ch - cd->channel) + 1;
318}
319
320/* Note 47 CFR 15.119 (h) Character Attributes: "(1) Transmission of
321 Attributes. A character may be transmitted with any or all of four
322 attributes: Color, italics, underline, and flash. All of these
323 attributes are set by control codes included in the received
324 data. An attribute will remain in effect until changed by another
325 control code or until the end of the row is reached. Each row
326 begins with a control code which sets the color and underline
327 attributes. (White non-underlined is the default display attribute
328 if no Preamble Address Code is received before the first character
329 on an empty row.) Attributes are not affected by transparent spaces
330 within a row. (i) All Mid-Row Codes and the Flash On command are
331 spacing attributes which appear in the display just as if a
332 standard space (20h) had been received. Preamble Address Codes are
333 non-spacing and will not alter any attributes when used to position
334 the cursor in the midst of a row of characters. (ii) The color
335 attribute has the highest priority and can only be changed by the
336 Mid-Row Code of another color. Italics has the next highest
337 priority. If characters with both color and italics are desired,
338 the italics Mid-Row Code must follow the color assignment. Any
339 color Mid-Row Code will turn off italics. If the least significant
340 bit of a Preamble Address Code or of a color or italics Mid-Row
341 Code is a 1 (high), underlining is turned on. If that bit is a 0
342 (low), underlining is off. (iii) The flash attribute is transmitted
343 as a Miscellaneous Control Code. The Flash On command will not
344 alter the status of the color, italics, or underline
345 attributes. However, any coloror italics Mid-Row Code will turn off
346 flash. (iv) Thus, for example, if a red, italicized, underlined,
347 flashing character is desired, the attributes must be received in
348 the following order: a red Mid-Row or Preamble Address Code, an
349 italics Mid-Row Code with underline bit, and the Flash On
350 command. The character will then be preceded by three spaces (two
351 if red was assigned via a Preamble Address Code)."
352
353 EIA 608-B Annex C.7 Preamble Address Codes and Tab Offsets
354 (Regulatory/Preferred): "In general, Preamble Address Codes (PACs)
355 have no immediate effect on the display. A major exception is the
356 receipt of a PAC during roll-up captioning. In that case, if the
357 base row designated in the PAC is not the same as the current base
358 row, the display shall be moved immediately to the new base
359 row. [...]
360
361
362cc_channel_num (struct cc_decoder * cd,
363 struct cc_channel * ch)
364indenting PAC carries the attributes of white,
365 non-italicized, and it sets underlining on or off. Tab Offset
366 commands do not change these attributes. If an indenting PAC with
367 underline ON is received followed by a Tab Offset and by text, the
368 text shall be underlined (except as noted below). When a
369 displayable character is received, it is deposited at the current
370 cursor position. If there is already a displayable character in the
371 column immediately to the left, the new character assumes the
372 attributes of that character. The new character may be arriving as
373 the result of an indenting PAC (with or without a Tab Offset), and
374 that PAC may designate other attributes, but the new character is
375 forced to assume the attributes of the character immediately to its
376 left, and the PAC's attributes are ignored. If, when a displayable
377 character is received, it overwrites an existing PAC or mid-row
378 code, and there are already characters to the right of the new
379 character, these existing characters shall assume the same
380 attributes as the new character. This adoption can result in a
381 whole caption row suddenly changing color, underline, italics,
382 and/or flash attributes."
383
384 EIA 608-B Annex C.14 Special Cases Regarding Attributes
385 (Normative): "In most cases, Preamble Address Codes shall set
386 attributes for the caption elements they address. It is
387 theoretically possible for a service provider to use an indenting
388 PAC to start a row at Column 5 or greater, and then to use
389 Backspace to move the cursor to the left of the PAC into an area to
390 which no attributes have been assigned. It is also possible for a
391 roll-up row, having been created by a Carriage Return, to receive
392 characters with no PAC used to set attributes. In these cases, and
393 in any other case where no explicit attributes have been assigned,
394 the display shall be white, non-underlined, non-italicized, and
395 non-flashing. In case new displayable characters are received
396 immediately after a Delete to End of Row (DER), the display
397 attributes of the first deleted character shall remain in effect if
398 there is a displayable character to the left of the cursor;
399 otherwise, the most recently received PAC shall set the display
400 attributes."
401
402 47 CFR 15.119 (n) Glossary of terms: "(6) Displayable character:
403 Any letter, number or symbol which is defined for on-screen
404 display, plus the 20h space. [...] (13) Special characters:
405 Displayable characters (except for "transparent space") [...]" */
406
407static void
408cc_format_row (struct cc_decoder * cd,
409 struct vbi_char * cp,
410 struct cc_channel * ch,
411 unsigned int buffer,
412 unsigned int row,
413 vbi_bool to_upper,
414 vbi_bool padding)
415{
416 struct vbi_char ac;
417 unsigned int i;
418
419 cd = cd; /* unused */
420
421 /* 47 CFR 15.119 (h)(1). EIA 608-B Section 6.4. */
422 CLEAR (ac);
423 ac.foreground = VBI_WHITE;
424 ac.background = VBI_BLACK;
425
426 /* Shortcut. */
427 if (0 == (ch->dirty[buffer] & (1 << row))) {
428 vbi_char *end;
429
430 ac.unicode = 0x20;
431 ac.opacity = VBI_TRANSPARENT_SPACE;
432
433 end = cp + CC_MAX_COLUMNS;
434 if (padding)
435 end += 2;
436
437 while (cp < end)
438 *cp++ = ac;
439
440 return;
441 }
442
443 if (padding) {
444 ac.unicode = 0x20;
445 ac.opacity = VBI_TRANSPARENT_SPACE;
446 *cp++ = ac;
447 }
448
449 /* EIA 608-B Section 6.4. */
450 ac.opacity = VBI_OPAQUE;
451
452 for (i = CC_FIRST_COLUMN - 1; i <= CC_LAST_COLUMN; ++i) {
453 unsigned int color;
454 unsigned int c;
455
456 ac.unicode = 0x20;
457
458 c = ch->buffer[buffer][row][i];
459 if (0 == c) {
460 if (padding
461 && VBI_TRANSPARENT_SPACE != cp[-1].opacity
462 && 0x20 != cp[-1].unicode) {
463 /* Append a space with the same colors
464 and opacity (opaque or
465 transp. backgr.) as the text to the
466 left of it. */
467 *cp++ = ac;
468 /* We don't underline spaces, see
469 below. */
470 vbi_char_clear_attr (cp - 1, -1);
471 } else if (i > 0) {
472 *cp++ = ac;
473 cp[-1].opacity = VBI_TRANSPARENT_SPACE;
474 }
475
476 continue;
477 } else if (c < 0x1040) {
478 if (padding
479 && VBI_TRANSPARENT_SPACE == cp[-1].opacity) {
480 /* Prepend a space with the same
481 colors and opacity (opaque or
482 transp. backgr.) as the text to the
483 right of it. */
484 cp[-1] = ac;
485 /* We don't underline spaces, see
486 below. */
487 vbi_char_clear_attr (cp - 1, -1);
488 }
489
490 if ((c >= 'a' && c <= 'z')
491 || 0x7E == c /* n with tilde */) {
492 /* We do not force these characters to
493 upper case because the standard
494 character set includes upper case
495 versions of these characters and
496 lower case was probably
497 deliberately transmitted. */
498 ac.unicode = vbi_caption_unicode
499 (c, /* to_upper */ FALSE);
500 } else {
501 ac.unicode = vbi_caption_unicode
502 (c, to_upper);
503 }
504 } else if (c < 0x1120) {
505 /* Preamble Address Codes -- 001 crrr 1ri xxxu */
506
507 /* PAC is a non-spacing attribute and only
508 stored in the buffer at the addressed
509 column minus one if it replaces a
510 transparent space (EIA 608-B Annex C.7,
511 C.14). There's always a transparent space
512 to the left of the first column but we show
513 this zeroth column only if padding is
514 enabled. */
515 if (padding
516 && VBI_TRANSPARENT_SPACE != cp[-1].opacity
517 && 0x20 != cp[-1].unicode) {
518 /* See 0 == c. */
519 *cp++ = ac;
520 vbi_char_clear_attr (cp - 1, -1);
521 } else if (i > 0) {
522 *cp++ = ac;
523 cp[-1].opacity = VBI_TRANSPARENT_SPACE;
524 }
525
526 vbi_char_clear_attr (&ac, VBI_UNDERLINE | VBI_ITALIC);
527 if (c & 0x0001)
528 vbi_char_set_attr (&ac, VBI_UNDERLINE);
529 if (c & 0x0010) {
530 ac.foreground = VBI_WHITE;
531 } else {
532 color = (c >> 1) & 7;
533 if (7 == color) {
534 ac.foreground = VBI_WHITE;
535 vbi_char_set_attr (&ac, VBI_ITALIC);
536 } else {
537 ac.foreground = cc_color_map[color];
538 }
539 }
540
541 continue;
542 } else if (c < 0x1130) {
543 /* Mid-Row Codes -- 001 c001 010 xxxu */
544 /* 47 CFR 15.119 Mid-Row Codes table,
545 (h)(1)(ii), (h)(1)(iii). */
546
547 /* 47 CFR 15.119 (h)(1)(i), EIA 608-B Section
548 6.2: Mid-Row codes, FON, BT, FA and FAU are
549 set-at spacing attributes. */
550
551 vbi_char_clear_attr (&ac, -1);
552 if (c & 0x0001)
553 vbi_char_set_attr (&ac, VBI_UNDERLINE);
554 color = (c >> 1) & 7;
555 if (7 == color) {
556 vbi_char_set_attr (&ac, VBI_ITALIC);
557 } else {
558 ac.foreground = cc_color_map[color];
559 }
560 } else if (c < 0x1220) {
561 /* Special Characters -- 001 c001 011 xxxx */
562 /* 47 CFR 15.119 Character Set Table. */
563
564 if (padding
565 && VBI_TRANSPARENT_SPACE == cp[-1].opacity) {
566 cp[-1] = ac;
567 vbi_char_clear_attr (cp - 1, -1);
568 }
569
570 /* Note we already stored 0 instead of 0x1139
571 (transparent space) in the ch->buffer. */
572 ac.unicode = vbi_caption_unicode (c, to_upper);
573 } else if (c < 0x1428) {
574 /* Extended Character Set -- 001 c01x 01x xxxx */
575 /* EIA 608-B Section 6.4.2 */
576
577 if (padding
578 && VBI_TRANSPARENT_SPACE == cp[-1].opacity) {
579 cp[-1] = ac;
580 vbi_char_clear_attr (cp - 1, -1);
581 }
582
583 /* We do not force these characters to upper
584 case because the extended character set
585 includes upper case versions of all letters
586 and lower case was probably deliberately
587 transmitted. */
588 ac.unicode = vbi_caption_unicode
589 (c, /* to_upper */ FALSE);
590 } else if (c < 0x172D) {
591 /* FON Flash On -- 001 c10f 010 1000 */
592 /* 47 CFR 15.119 (h)(1)(iii). */
593
594 vbi_char_set_attr (&ac, VBI_FLASH);
595 } else if (c < 0x172E) {
596 /* BT Background Transparent -- 001 c111 010 1101 */
597 /* EIA 608-B Section 6.4. */
598
599 ac.opacity = VBI_TRANSPARENT_FULL;
600 } else if (c <= 0x172F) {
601 /* FA Foreground Black -- 001 c111 010 111u */
602 /* EIA 608-B Section 6.4. */
603
604 if (c & 0x0001)
605 vbi_char_set_attr (&ac, VBI_UNDERLINE);
606 ac.foreground = VBI_BLACK;
607 }
608
609 *cp++ = ac;
610
611 /* 47 CFR 15.119 and EIA 608-B are silent about
612 underlined spaces, but considering the example in
613 47 CFR (h)(1)(iv) which would produce something
614 ugly like "__text" I suppose we should not
615 underline them. For good measure we also clear the
616 invisible italic and flash attribute. */
617 if (0x20 == ac.unicode)
618 vbi_char_clear_attr (cp - 1, -1);
619 }
620
621 if (padding) {
622 ac.unicode = 0x20;
623 vbi_char_clear_attr (&ac, -1);
624
625 if (VBI_TRANSPARENT_SPACE != cp[-1].opacity
626 && 0x20 != cp[-1].unicode) {
627 *cp = ac;
628 } else {
629 ac.opacity = VBI_TRANSPARENT_SPACE;
630 *cp = ac;
631 }
632 }
633}
634
635typedef enum {
636 /**
637 * 47 CFR Section 15.119 requires caption decoders to roll
638 * caption smoothly: Nominally each character cell has a
639 * height of 13 field lines. When this flag is set the current
640 * caption should be displayed with a vertical offset of 12
641 * field lines, and after every 1001 / 30000 seconds the
642 * caption overlay should move up by one field line until the
643 * offset is zero. The roll rate should be no more than 0.433
644 * seconds/row for other character cell heights.
645 *
646 * The flag may be set again before the offset returned to
647 * zero. The caption overlay should jump to offset 12 in this
648 * case regardless.
649 */
650 VBI_START_ROLLING = (1 << 0)
651} vbi_cc_page_flags;
652
653static void
654cc_display_event (struct cc_decoder * cd,
655 struct cc_channel * ch,
656 vbi_cc_page_flags flags)
657{
658 cd = cd; /* unused */
659 ch = ch;
660 flags = flags;
661}
662
663/* This decoder is mainly designed to overlay caption onto live video,
664 but to create transcripts we also offer an event every time a line
665 of caption is complete. The event occurs when certain control codes
666 are received.
667
668 In POP_ON mode we send the event upon reception of EOC, which swaps
669 the displayed and non-displayed memory.
670
671 In ROLL_UP and TEXT mode captioners are not expected to display new
672 text by erasing and overwriting a row with PAC, TOx, BS and DER so
673 we ignore these codes. In ROLL_UP mode CR, EDM, EOC, RCL and RDC
674 complete a line. CR moves the cursor to a new row, EDM erases the
675 displayed memory. The remaining codes switch to POP_ON or PAINT_ON
676 mode. In TEXT mode CR and TR are our line completion indicators. CR
677 works as above and TR erases the displayed memory. EDM, EOC, RDC,
678 RCL and RUx have no effect on TEXT buffers.
679
680 In PAINT_ON mode RDC never erases the displayed memory and CR has
681 no function. Instead captioners can freely position the cursor and
682 erase or overwrite (parts of) rows with PAC, TOx, BS and DER, or
683 erase all rows with EDM. We send an event on PAC, EDM, EOC, RCL and
684 RUx, provided the characters (including spacing attributes) in the
685 current row changed since the last event. PAC is the only control
686 code which can move the cursor to the left and/or to a new row, and
687 likely to introduce a new line. EOC, RCL and RUx switch to POP_ON
688 or ROLL_UP mode. */
689
690static void
691cc_stream_event (struct cc_decoder * cd,
692 struct cc_channel * ch,
693 unsigned int first_row,
694 unsigned int last_row)
695{
696 vbi_pgno channel;
697 unsigned int row;
698
699 channel = cc_channel_num (cd, ch);
700
701 for (row = first_row; row <= last_row; ++row) {
702 struct vbi_char text[36];
703 unsigned int end;
704
705 cc_format_row (cd, text, ch,
706 ch->displayed_buffer,
707 row, /* to_upper */ FALSE,
708 /* padding */ FALSE);
709
710 for (end = 32; end > 0; --end) {
711 if (VBI_TRANSPARENT_SPACE != text[end - 1].opacity)
712 break;
713 }
714
715 if (0 == end)
716 continue;
717 }
718
719 cc_timestamp_reset (&ch->timestamp_c0);
720}
721
722static void
723cc_put_char (struct cc_decoder * cd,
724 struct cc_channel * ch,
725 int c,
726 vbi_bool displayable,
727 vbi_bool backspace)
728{
729 uint16_t *text;
730 unsigned int curr_buffer;
731 unsigned int row;
732 unsigned int column;
733
734 /* 47 CFR Section 15.119 (f)(1), (f)(2), (f)(3). */
735 curr_buffer = ch->displayed_buffer
736 ^ (CC_MODE_POP_ON == ch->mode);
737
738 row = ch->curr_row;
739 column = ch->curr_column;
740
741 if (unlikely (backspace)) {
742 /* 47 CFR 15.119 (f)(1)(vi), (f)(2)(ii),
743 (f)(3)(i). EIA 608-B Section 6.4.2, 7.4. */
744 if (column > CC_FIRST_COLUMN)
745 --column;
746 } else {
747 /* 47 CFR 15.119 (f)(1)(v), (f)(1)(vi), (f)(2)(ii),
748 (f)(3)(i). EIA 608-B Section 7.4. */
749 if (column < CC_LAST_COLUMN)
750 ch->curr_column = column + 1;
751 }
752
753 text = &ch->buffer[curr_buffer][row][0];
754 text[column] = c;
755
756 /* Send a display update event when the displayed buffer of
757 the current channel changed, but no more than once for each
758 pair of Closed Caption bytes. */
759 /* XXX This may not be a visible change, but such cases are
760 rare and we'd need something close to format_row() to be
761 sure. */
762 if (CC_MODE_POP_ON != ch->mode) {
763 cd->event_pending = ch;
764 }
765
766 if (likely (displayable)) {
767 /* Note EIA 608-B Annex C.7, C.14. */
768 if (CC_FIRST_COLUMN == column
769 || 0 == text[column - 1]) {
770 /* Note last_pac may be 0 as well. */
771 text[column - 1] = ch->last_pac;
772 }
773
774 if (c >= 'a' && c <= 'z') {
775 ch->uppercase_predictor = 0;
776 } else if (c >= 'A' && c <= 'Z') {
777 unsigned int up;
778
779 up = ch->uppercase_predictor + 1;
780 if (up > 0)
781 ch->uppercase_predictor = up;
782 }
783 } else if (unlikely (0 == c)) {
784 unsigned int i;
785
786 /* This is Special Character "Transparent space". */
787
788 for (i = CC_FIRST_COLUMN; i <= CC_LAST_COLUMN; ++i)
789 c |= ch->buffer[curr_buffer][row][i];
790
791 ch->dirty[curr_buffer] &= ~((0 == c) << row);
792
793 return;
794 }
795
796 assert (sizeof (ch->dirty[0]) * 8 - 1 >= CC_MAX_ROWS);
797 ch->dirty[curr_buffer] |= 1 << row;
798
799 if (ch->timestamp_c0.pts < 0
800 && 0 == (ch->timestamp_c0.sys.tv_sec
801 | ch->timestamp_c0.sys.tv_usec)) {
802 ch->timestamp_c0 = cd->timestamp;
803 }
804}
805
806static void
807cc_ext_control_code (struct cc_decoder * cd,
808 struct cc_channel * ch,
809 unsigned int c2)
810{
811 unsigned int column;
812
813 switch (c2) {
814 case 0x21: /* TO1 */
815 case 0x22: /* TO2 */
816 case 0x23: /* TO3 Tab Offset -- 001 c111 010 00xx */
817 /* 47 CFR 15.119 (e)(1)(ii). EIA 608-B Section 7.4,
818 Annex C.7. */
819 column = ch->curr_column + (c2 & 3);
820 ch->curr_column = MIN (column,
821 (unsigned int) CC_LAST_COLUMN);
822 break;
823
824 case 0x24: /* Select standard character set in normal size */
825 case 0x25: /* Select standard character set in double size */
826 case 0x26: /* Select first private character set */
827 case 0x27: /* Select second private character set */
828 case 0x28: /* Select character set GB 2312-80 (Chinese) */
829 case 0x29: /* Select character set KSC 5601-1987 (Korean) */
830 case 0x2A: /* Select first registered character set. */
831 /* EIA 608-B Section 6.3 Closed Group Extensions. */
832 break;
833
834 case 0x2D: /* BT Background Transparent -- 001 c111 010 1101 */
835 case 0x2E: /* FA Foreground Black -- 001 c111 010 1110 */
836 case 0x2F: /* FAU Foregr. Black Underl. -- 001 c111 010 1111 */
837 /* EIA 608-B Section 6.2. */
838 cc_put_char (cd, ch, 0x1700 | c2,
839 /* displayable */ FALSE,
840 /* backspace */ TRUE);
841 break;
842
843 default:
844 /* 47 CFR Section 15.119 (j): Ignore. */
845 break;
846 }
847}
848
849/* Send a stream event if the current row has changed since the last
850 stream event. This is necessary in paint-on mode where CR has no
851 function and captioners can freely position the cursor to erase or
852 overwrite (parts of) rows. */
853static void
854cc_stream_event_if_changed (struct cc_decoder * cd,
855 struct cc_channel * ch)
856{
857 unsigned int curr_buffer;
858 unsigned int row;
859 unsigned int i;
860
861 curr_buffer = ch->displayed_buffer;
862 row = ch->curr_row;
863
864 if (0 == (ch->dirty[curr_buffer] & (1 << row)))
865 return;
866
867 for (i = CC_FIRST_COLUMN; i <= CC_LAST_COLUMN; ++i) {
868 unsigned int c1;
869 unsigned int c2;
870
871 c1 = ch->buffer[curr_buffer][row][i];
872 if (c1 >= 0x1040) {
873 if (c1 < 0x1120) {
874 c1 = 0; /* PAC -- non-spacing */
875 } else if (c1 < 0x1130 || c1 >= 0x1428) {
876 /* MR, FON, BT, FA, FAU -- spacing */
877 c1 = 0x20;
878 }
879 }
880
881 c2 = ch->buffer[2][row][i];
882 if (c2 >= 0x1040) {
883 if (c2 < 0x1120) {
884 c2 = 0;
885 } else if (c2 < 0x1130 || c2 >= 0x1428) {
886 c1 = 0x20;
887 }
888 }
889
890 if (c1 != c2) {
891 cc_stream_event (cd, ch, row, row);
892
893 memcpy (ch->buffer[2][row],
894 ch->buffer[curr_buffer][row],
895 sizeof (ch->buffer[0][0]));
896
897 ch->dirty[2] = ch->dirty[curr_buffer];
898
899 return;
900 }
901 }
902}
903
904static void
905cc_end_of_caption (struct cc_decoder * cd,
906 struct cc_channel * ch)
907{
908 unsigned int curr_buffer;
909 unsigned int row;
910
911 /* EOC End Of Caption -- 001 c10f 010 1111 */
912
913 curr_buffer = ch->displayed_buffer;
914
915 switch (ch->mode) {
916 case CC_MODE_UNKNOWN:
917 case CC_MODE_POP_ON:
918 break;
919
920 case CC_MODE_ROLL_UP:
921 row = ch->curr_row;
922 if (0 != (ch->dirty[curr_buffer] & (1 << row)))
923 cc_stream_event (cd, ch, row, row);
924 break;
925
926 case CC_MODE_PAINT_ON:
927 cc_stream_event_if_changed (cd, ch);
928 break;
929
930 case CC_MODE_TEXT:
931 /* Not reached. (ch is a caption channel.) */
932 return;
933 }
934
935 ch->displayed_buffer = curr_buffer ^= 1;
936
937 /* 47 CFR Section 15.119 (f)(2). */
938 ch->mode = CC_MODE_POP_ON;
939
940 if (0 != ch->dirty[curr_buffer]) {
941 ch->timestamp_c0 = cd->timestamp;
942
943 cc_stream_event (cd, ch,
944 CC_FIRST_ROW,
945 CC_LAST_ROW);
946
947 cc_display_event (cd, ch, 0);
948 }
949}
950
951static void
952cc_carriage_return (struct cc_decoder * cd,
953 struct cc_channel * ch)
954{
955 unsigned int curr_buffer;
956 unsigned int row;
957 unsigned int window_rows;
958 unsigned int first_row;
959
960 /* CR Carriage Return -- 001 c10f 010 1101 */
961
962 curr_buffer = ch->displayed_buffer;
963 row = ch->curr_row;
964
965 switch (ch->mode) {
966 case CC_MODE_UNKNOWN:
967 return;
968
969 case CC_MODE_ROLL_UP:
970 /* 47 CFR Section 15.119 (f)(1)(iii). */
971 ch->curr_column = CC_FIRST_COLUMN;
972
973 /* 47 CFR 15.119 (f)(1): "The cursor always remains on
974 the base row." */
975
976 /* XXX Spec? */
977 ch->last_pac = 0;
978
979 /* No event if the buffer contains only
980 TRANSPARENT_SPACEs. */
981 if (0 == ch->dirty[curr_buffer])
982 return;
983
984 window_rows = MIN (row + 1 - CC_FIRST_ROW,
985 ch->window_rows);
986 break;
987
988 case CC_MODE_POP_ON:
989 case CC_MODE_PAINT_ON:
990 /* 47 CFR 15.119 (f)(2)(i), (f)(3)(i): No effect. */
991 return;
992
993 case CC_MODE_TEXT:
994 /* 47 CFR Section 15.119 (f)(1)(iii). */
995 ch->curr_column = CC_FIRST_COLUMN;
996
997 /* XXX Spec? */
998 ch->last_pac = 0;
999
1000 /* EIA 608-B Section 7.4: "When Text Mode has
1001 initially been selected and the specified Text
1002 memory is empty, the cursor starts at the topmost
1003 row, Column 1, and moves down to Column 1 on the
1004 next row each time a Carriage Return is received
1005 until the last available row is reached. A variety
1006 of methods may be used to accomplish the scrolling,
1007 provided that the text is legible while moving. For
1008 example, as soon as all of the available rows of
1009 text are on the screen, Text Mode switches to the
1010 standard roll-up type of presentation." */
1011
1012 if (CC_LAST_ROW != row) {
1013 if (0 != (ch->dirty[curr_buffer] & (1 << row))) {
1014 cc_stream_event (cd, ch, row, row);
1015 }
1016
1017 ch->curr_row = row + 1;
1018
1019 return;
1020 }
1021
1022 /* No event if the buffer contains all
1023 TRANSPARENT_SPACEs. */
1024 if (0 == ch->dirty[curr_buffer])
1025 return;
1026
1027 window_rows = CC_MAX_ROWS;
1028
1029 break;
1030 }
1031
1032 /* 47 CFR Section 15.119 (f)(1)(iii). In roll-up mode: "Each
1033 time a Carriage Return is received, the text in the top row
1034 of the window is erased from memory and from the display or
1035 scrolled off the top of the window. The remaining rows of
1036 text are each rolled up into the next highest row in the
1037 window, leaving the base row blank and ready to accept new
1038 text." */
1039
1040 if (0 != (ch->dirty[curr_buffer] & (1 << row))) {
1041 cc_stream_event (cd, ch, row, row);
1042 }
1043
1044 first_row = row + 1 - window_rows;
1045 memmove (ch->buffer[curr_buffer][first_row],
1046 ch->buffer[curr_buffer][first_row + 1],
1047 (window_rows - 1) * sizeof (ch->buffer[0][0]));
1048
1049 ch->dirty[curr_buffer] >>= 1;
1050
1051 memset (ch->buffer[curr_buffer][row], 0,
1052 sizeof (ch->buffer[0][0]));
1053
1054 cc_display_event (cd, ch, VBI_START_ROLLING);
1055}
1056
1057static void
1058cc_erase_memory (struct cc_decoder * cd,
1059 struct cc_channel * ch,
1060 unsigned int buffer)
1061{
1062 if (0 != ch->dirty[buffer]) {
1063 CLEAR (ch->buffer[buffer]);
1064
1065 ch->dirty[buffer] = 0;
1066
1067 if (buffer == ch->displayed_buffer)
1068 cc_display_event (cd, ch, 0);
1069 }
1070}
1071
1072static void
1073cc_erase_displayed_memory (struct cc_decoder * cd,
1074 struct cc_channel * ch)
1075{
1076 unsigned int row;
1077
1078 /* EDM Erase Displayed Memory -- 001 c10f 010 1100 */
1079
1080 switch (ch->mode) {
1081 case CC_MODE_UNKNOWN:
1082 /* We have not received EOC, RCL, RDC or RUx yet, but
1083 ch is valid. */
1084 break;
1085
1086 case CC_MODE_ROLL_UP:
1087 row = ch->curr_row;
1088 if (0 != (ch->dirty[ch->displayed_buffer] & (1 << row)))
1089 cc_stream_event (cd, ch, row, row);
1090 break;
1091
1092 case CC_MODE_PAINT_ON:
1093 cc_stream_event_if_changed (cd, ch);
1094 break;
1095
1096 case CC_MODE_POP_ON:
1097 /* Nothing to do. */
1098 break;
1099
1100 case CC_MODE_TEXT:
1101 /* Not reached. (ch is a caption channel.) */
1102 return;
1103 }
1104
1105 /* May send a display event. */
1106 cc_erase_memory (cd, ch, ch->displayed_buffer);
1107}
1108
1109static void
1110cc_text_restart (struct cc_decoder * cd,
1111 struct cc_channel * ch)
1112{
1113 unsigned int curr_buffer;
1114 unsigned int row;
1115
1116 /* TR Text Restart -- 001 c10f 010 1010 */
1117
1118 curr_buffer = ch->displayed_buffer;
1119 row = ch->curr_row;
1120
1121 /* ch->mode is invariably CC_MODE_TEXT. */
1122
1123 if (0 != (ch->dirty[curr_buffer] & (1 << row))) {
1124 cc_stream_event (cd, ch, row, row);
1125 }
1126
1127 /* EIA 608-B Section 7.4. */
1128 /* May send a display event. */
1129 cc_erase_memory (cd, ch, ch->displayed_buffer);
1130
1131 /* EIA 608-B Section 7.4. */
1132 ch->curr_row = CC_FIRST_ROW;
1133 ch->curr_column = CC_FIRST_COLUMN;
1134}
1135
1136static void
1137cc_resume_direct_captioning (struct cc_decoder * cd,
1138 struct cc_channel * ch)
1139{
1140 unsigned int curr_buffer;
1141 unsigned int row;
1142
1143 /* RDC Resume Direct Captioning -- 001 c10f 010 1001 */
1144
1145 /* 47 CFR 15.119 (f)(1)(x), (f)(2)(vi) and EIA 608-B Annex
1146 B.7: Does not erase memory, does not move the cursor when
1147 resuming after a Text transmission.
1148
1149 XXX If ch->mode is unknown, roll-up or pop-on, what shall
1150 we do if no PAC is received between RDC and the text? */
1151
1152 curr_buffer = ch->displayed_buffer;
1153 row = ch->curr_row;
1154
1155 switch (ch->mode) {
1156 case CC_MODE_ROLL_UP:
1157 if (0 != (ch->dirty[curr_buffer] & (1 << row)))
1158 cc_stream_event (cd, ch, row, row);
1159
1160 /* fall through */
1161
1162 case CC_MODE_UNKNOWN:
1163 case CC_MODE_POP_ON:
1164 /* No change since last stream_event(). */
1165 memcpy (ch->buffer[2], ch->buffer[curr_buffer],
1166 sizeof (ch->buffer[2]));
1167 break;
1168
1169 case CC_MODE_PAINT_ON:
1170 /* Mode continues. */
1171 break;
1172
1173 case CC_MODE_TEXT:
1174 /* Not reached. (ch is a caption channel.) */
1175 return;
1176 }
1177
1178 ch->mode = CC_MODE_PAINT_ON;
1179}
1180
1181static void
1182cc_resize_window (struct cc_decoder * cd,
1183 struct cc_channel * ch,
1184 unsigned int new_rows)
1185{
1186 unsigned int curr_buffer;
1187 unsigned int max_rows;
1188 unsigned int old_rows;
1189 unsigned int row1;
1190
1191 curr_buffer = ch->displayed_buffer;
1192
1193 /* No event if the buffer contains all TRANSPARENT_SPACEs. */
1194 if (0 == ch->dirty[curr_buffer])
1195 return;
1196
1197 row1 = ch->curr_row + 1;
1198 max_rows = row1 - CC_FIRST_ROW;
1199 old_rows = MIN (ch->window_rows, max_rows);
1200 new_rows = MIN (new_rows, max_rows);
1201
1202 /* Nothing to do unless the window shrinks. */
1203 if (0 == new_rows || new_rows >= old_rows)
1204 return;
1205
1206 memset (&ch->buffer[curr_buffer][row1 - old_rows][0], 0,
1207 (old_rows - new_rows)
1208 * sizeof (ch->buffer[0][0]));
1209
1210 ch->dirty[curr_buffer] &= -1 << (row1 - new_rows);
1211
1212 cc_display_event (cd, ch, 0);
1213}
1214
1215static void
1216cc_roll_up_caption (struct cc_decoder * cd,
1217 struct cc_channel * ch,
1218 unsigned int c2)
1219{
1220 unsigned int window_rows;
1221
1222 /* Roll-Up Captions -- 001 c10f 010 01xx */
1223
1224 window_rows = (c2 & 7) - 3; /* 2, 3, 4 */
1225
1226 switch (ch->mode) {
1227 case CC_MODE_ROLL_UP:
1228 /* 47 CFR 15.119 (f)(1)(iv). */
1229 /* May send a display event. */
1230 cc_resize_window (cd, ch, window_rows);
1231
1232 /* fall through */
1233
1234 case CC_MODE_UNKNOWN:
1235 ch->mode = CC_MODE_ROLL_UP;
1236 ch->window_rows = window_rows;
1237
1238 /* 47 CFR 15.119 (f)(1)(ix): No cursor movements,
1239 no memory erasing. */
1240
1241 break;
1242
1243 case CC_MODE_PAINT_ON:
1244 cc_stream_event_if_changed (cd, ch);
1245
1246 /* fall through */
1247
1248 case CC_MODE_POP_ON:
1249 ch->mode = CC_MODE_ROLL_UP;
1250 ch->window_rows = window_rows;
1251
1252 /* 47 CFR 15.119 (f)(1)(ii). */
1253 ch->curr_row = CC_LAST_ROW;
1254 ch->curr_column = CC_FIRST_COLUMN;
1255
1256 /* 47 CFR 15.119 (f)(1)(x). */
1257 /* May send a display event. */
1258 cc_erase_memory (cd, ch, ch->displayed_buffer);
1259 cc_erase_memory (cd, ch, ch->displayed_buffer ^ 1);
1260
1261 break;
1262
1263 case CC_MODE_TEXT:
1264 /* Not reached. (ch is a caption channel.) */
1265 return;
1266 }
1267}
1268
1269static void
1270cc_delete_to_end_of_row (struct cc_decoder * cd,
1271 struct cc_channel * ch)
1272{
1273 unsigned int curr_buffer;
1274 unsigned int row;
1275
1276 /* DER Delete To End Of Row -- 001 c10f 010 0100 */
1277
1278 /* 47 CFR 15.119 (f)(1)(vii), (f)(2)(iii), (f)(3)(ii) and EIA
1279 608-B Section 7.4: In all caption modes and Text mode
1280 "[the] Delete to End of Row command will erase from memory
1281 any characters or control codes starting at the current
1282 cursor location and in all columns to its right on the same
1283 row." */
1284
1285 curr_buffer = ch->displayed_buffer
1286 ^ (CC_MODE_POP_ON == ch->mode);
1287
1288 row = ch->curr_row;
1289
1290 /* No event if the row contains only TRANSPARENT_SPACEs. */
1291 if (0 != (ch->dirty[curr_buffer] & (1 << row))) {
1292 unsigned int column;
1293 unsigned int i;
1294 uint16_t c;
1295
1296 column = ch->curr_column;
1297
1298 memset (&ch->buffer[curr_buffer][row][column], 0,
1299 (CC_LAST_COLUMN - column + 1)
1300 * sizeof (ch->buffer[0][0][0]));
1301
1302 c = 0;
1303 for (i = CC_FIRST_COLUMN; i < column; ++i)
1304 c |= ch->buffer[curr_buffer][row][i];
1305
1306 ch->dirty[curr_buffer] &= ~((0 == c) << row);
1307
1308 cc_display_event (cd, ch, 0);
1309 }
1310}
1311
1312static void
1313cc_backspace (struct cc_decoder * cd,
1314 struct cc_channel * ch)
1315{
1316 unsigned int curr_buffer;
1317 unsigned int row;
1318 unsigned int column;
1319
1320 /* BS Backspace -- 001 c10f 010 0001 */
1321
1322 /* 47 CFR Section 15.119 (f)(1)(vi), (f)(2)(ii), (f)(3)(i) and
1323 EIA 608-B Section 7.4. */
1324 column = ch->curr_column;
1325 if (column <= CC_FIRST_COLUMN)
1326 return;
1327
1328 ch->curr_column = --column;
1329
1330 curr_buffer = ch->displayed_buffer
1331 ^ (CC_MODE_POP_ON == ch->mode);
1332
1333 row = ch->curr_row;
1334
1335 /* No event if there's no visible effect. */
1336 if (0 != ch->buffer[curr_buffer][row][column]) {
1337 unsigned int i;
1338 uint16_t c;
1339
1340 /* 47 CFR 15.119 (f), (f)(1)(vi), (f)(2)(ii) and EIA
1341 608-B Section 7.4. */
1342 ch->buffer[curr_buffer][row][column] = 0;
1343
1344 c = 0;
1345 for (i = CC_FIRST_COLUMN; i <= CC_LAST_COLUMN; ++i)
1346 c |= ch->buffer[curr_buffer][row][i];
1347
1348 ch->dirty[curr_buffer] &= ~((0 == c) << row);
1349
1350 cc_display_event (cd, ch, 0);
1351 }
1352}
1353
1354static void
1355cc_resume_caption_loading (struct cc_decoder * cd,
1356 struct cc_channel * ch)
1357{
1358 unsigned int row;
1359
1360 /* RCL Resume Caption Loading -- 001 c10f 010 0000 */
1361
1362 switch (ch->mode) {
1363 case CC_MODE_UNKNOWN:
1364 case CC_MODE_POP_ON:
1365 break;
1366
1367 case CC_MODE_ROLL_UP:
1368 row = ch->curr_row;
1369 if (0 != (ch->dirty[ch->displayed_buffer] & (1 << row)))
1370 cc_stream_event (cd, ch, row, row);
1371 break;
1372
1373 case CC_MODE_PAINT_ON:
1374 cc_stream_event_if_changed (cd, ch);
1375 break;
1376
1377 case CC_MODE_TEXT:
1378 /* Not reached. (ch is a caption channel.) */
1379 return;
1380 }
1381
1382 /* 47 CFR 15.119 (f)(1)(x): Does not erase memory.
1383 (f)(2)(iv): Cursor position remains unchanged. */
1384
1385 ch->mode = CC_MODE_POP_ON;
1386}
1387
1388
1389
1390/* Note curr_ch is invalid if UNKNOWN_CC_CHANNEL == cd->cc.curr_ch_num. */
1391static struct cc_channel *
1392cc_switch_channel (struct cc_decoder * cd,
1393 struct cc_channel * curr_ch,
1394 vbi_pgno new_ch_num,
1395 enum field_num f)
1396{
1397 struct cc_channel *new_ch;
1398
1399 if (UNKNOWN_CC_CHANNEL != cd->curr_ch_num[f]
1400 && CC_MODE_UNKNOWN != curr_ch->mode) {
1401 /* XXX Force a display update if we do not send events
1402 on every display change. */
1403 }
1404
1405 cd->curr_ch_num[f] = new_ch_num;
1406 new_ch = &cd->channel[new_ch_num - VBI_CAPTION_CC1];
1407
1408 return new_ch;
1409}
1410
1411/* Note ch is invalid if UNKNOWN_CC_CHANNEL == cd->cc.curr_ch_num[f]. */
1412static void
1413cc_misc_control_code (struct cc_decoder * cd,
1414 struct cc_channel * ch,
1415 unsigned int c2,
1416 unsigned int ch_num0,
1417 enum field_num f)
1418{
1419 unsigned int new_ch_num;
1420
1421 /* Misc Control Codes -- 001 c10f 010 xxxx */
1422
1423 /* c = channel (0 -> CC1/CC3/T1/T3, 1 -> CC2/CC4/T2/T4)
1424 -- 47 CFR Section 15.119, EIA 608-B Section 7.7.
1425 f = field (0 -> F1, 1 -> F2)
1426 -- EIA 608-B Section 8.4, 8.5. */
1427
1428 /* XXX The f flag is intended to detect accidential field
1429 swapping and we should use it for that purpose. */
1430
1431 switch (c2 & 15) {
1432 case 0: /* RCL Resume Caption Loading -- 001 c10f 010 0000 */
1433 /* 47 CFR 15.119 (f)(2) and EIA 608-B Section 7.7. */
1434 new_ch_num = VBI_CAPTION_CC1 + (ch_num0 & 3);
1435 ch = cc_switch_channel (cd, ch, new_ch_num, f);
1436 cc_resume_caption_loading (cd, ch);
1437 break;
1438
1439 case 1: /* BS Backspace -- 001 c10f 010 0001 */
1440 if (UNKNOWN_CC_CHANNEL == cd->curr_ch_num[f]
1441 || CC_MODE_UNKNOWN == ch->mode)
1442 break;
1443 cc_backspace (cd, ch);
1444 break;
1445
1446 case 2: /* reserved (formerly AOF Alarm Off) */
1447 case 3: /* reserved (formerly AON Alarm On) */
1448 break;
1449
1450 case 4: /* DER Delete To End Of Row -- 001 c10f 010 0100 */
1451 if (UNKNOWN_CC_CHANNEL == cd->curr_ch_num[f]
1452 || CC_MODE_UNKNOWN == ch->mode)
1453 break;
1454 cc_delete_to_end_of_row (cd, ch);
1455 break;
1456
1457 case 5: /* RU2 */
1458 case 6: /* RU3 */
1459 case 7: /* RU4 Roll-Up Captions -- 001 c10f 010 01xx */
1460 /* 47 CFR 15.119 (f)(1) and EIA 608-B Section 7.7. */
1461 new_ch_num = VBI_CAPTION_CC1 + (ch_num0 & 3);
1462 ch = cc_switch_channel (cd, ch, new_ch_num, f);
1463 cc_roll_up_caption (cd, ch, c2);
1464 break;
1465
1466 case 8: /* FON Flash On -- 001 c10f 010 1000 */
1467 if (UNKNOWN_CC_CHANNEL == cd->curr_ch_num[f]
1468 || CC_MODE_UNKNOWN == ch->mode)
1469 break;
1470
1471 /* 47 CFR 15.119 (h)(1)(i): Spacing attribute. */
1472 cc_put_char (cd, ch, 0x1428,
1473 /* displayable */ FALSE,
1474 /* backspace */ FALSE);
1475 break;
1476
1477 case 9: /* RDC Resume Direct Captioning -- 001 c10f 010 1001 */
1478 /* 47 CFR 15.119 (f)(3) and EIA 608-B Section 7.7. */
1479 new_ch_num = VBI_CAPTION_CC1 + (ch_num0 & 3);
1480 ch = cc_switch_channel (cd, ch, new_ch_num, f);
1481 cc_resume_direct_captioning (cd, ch);
1482 break;
1483
1484 case 10: /* TR Text Restart -- 001 c10f 010 1010 */
1485 /* EIA 608-B Section 7.4. */
1486 new_ch_num = VBI_CAPTION_T1 + (ch_num0 & 3);
1487 ch = cc_switch_channel (cd, ch, new_ch_num, f);
1488 cc_text_restart (cd, ch);
1489 break;
1490
1491 case 11: /* RTD Resume Text Display -- 001 c10f 010 1011 */
1492 /* EIA 608-B Section 7.4. */
1493 new_ch_num = VBI_CAPTION_T1 + (ch_num0 & 3);
1494 ch = cc_switch_channel (cd, ch, new_ch_num, f);
1495 /* ch->mode is invariably CC_MODE_TEXT. */
1496 break;
1497
1498 case 12: /* EDM Erase Displayed Memory -- 001 c10f 010 1100 */
1499 /* 47 CFR 15.119 (f). EIA 608-B Section 7.7 and Annex
1500 B.7: "[The] command shall be acted upon as
1501 appropriate for caption processing without
1502 terminating the Text Mode data stream." */
1503
1504 /* We need not check cd->curr_ch_num because bit 2 is
1505 implied, bit 1 is the known field number and bit 0
1506 is coded in the control code. */
1507 ch = &cd->channel[ch_num0 & 3];
1508
1509 cc_erase_displayed_memory (cd, ch);
1510
1511 break;
1512
1513 case 13: /* CR Carriage Return -- 001 c10f 010 1101 */
1514 if (UNKNOWN_CC_CHANNEL == cd->curr_ch_num[f])
1515 break;
1516 cc_carriage_return (cd, ch);
1517 break;
1518
1519 case 14: /* ENM Erase Non-Displayed Memory -- 001 c10f 010 1110 */
1520 /* 47 CFR 15.119 (f)(2)(v). EIA 608-B Section 7.7 and
1521 Annex B.7: "[The] command shall be acted upon as
1522 appropriate for caption processing without
1523 terminating the Text Mode data stream." */
1524
1525 /* See EDM. */
1526 ch = &cd->channel[ch_num0 & 3];
1527
1528 cc_erase_memory (cd, ch, ch->displayed_buffer ^ 1);
1529
1530 break;
1531
1532 case 15: /* EOC End Of Caption -- 001 c10f 010 1111 */
1533 /* 47 CFR 15.119 (f), (f)(2), (f)(3)(iv) and EIA 608-B
1534 Section 7.7, Annex C.11. */
1535 new_ch_num = VBI_CAPTION_CC1 + (ch_num0 & 3);
1536 ch = cc_switch_channel (cd, ch, new_ch_num, f);
1537 cc_end_of_caption (cd, ch);
1538 break;
1539 }
1540}
1541
1542static void
1543cc_move_window (struct cc_decoder * cd,
1544 struct cc_channel * ch,
1545 unsigned int new_base_row)
1546{
1547 uint8_t *base;
1548 unsigned int curr_buffer;
1549 unsigned int bytes_per_row;
1550 unsigned int old_max_rows;
1551 unsigned int new_max_rows;
1552 unsigned int copy_bytes;
1553 unsigned int erase_begin;
1554 unsigned int erase_end;
1555
1556 curr_buffer = ch->displayed_buffer;
1557
1558 /* No event if we do not move the window or the buffer
1559 contains only TRANSPARENT_SPACEs. */
1560 if (new_base_row == ch->curr_row
1561 || 0 == ch->dirty[curr_buffer])
1562 return;
1563
1564 base = (void *) &ch->buffer[curr_buffer][CC_FIRST_ROW][0];
1565 bytes_per_row = sizeof (ch->buffer[0][0]);
1566
1567 old_max_rows = ch->curr_row + 1 - CC_FIRST_ROW;
1568 new_max_rows = new_base_row + 1 - CC_FIRST_ROW;
1569 copy_bytes = MIN (MIN (old_max_rows, new_max_rows),
1570 ch->window_rows) * bytes_per_row;
1571
1572 if (new_base_row < ch->curr_row) {
1573 erase_begin = (new_base_row + 1) * bytes_per_row;
1574 erase_end = (ch->curr_row + 1) * bytes_per_row;
1575
1576 memmove (base + erase_begin - copy_bytes,
1577 base + erase_end - copy_bytes, copy_bytes);
1578
1579 ch->dirty[curr_buffer] >>= ch->curr_row - new_base_row;
1580 } else {
1581 erase_begin = (ch->curr_row + 1) * bytes_per_row
1582 - copy_bytes;
1583 erase_end = (new_base_row + 1) * bytes_per_row
1584 - copy_bytes;
1585
1586 memmove (base + erase_end,
1587 base + erase_begin, copy_bytes);
1588
1589 ch->dirty[curr_buffer] <<= new_base_row - ch->curr_row;
1590 ch->dirty[curr_buffer] &= CC_ALL_ROWS_MASK;
1591 }
1592
1593 memset (base + erase_begin, 0, erase_end - erase_begin);
1594
1595 cc_display_event (cd, ch, 0);
1596}
1597
1598static void
1599cc_preamble_address_code (struct cc_decoder * cd,
1600 struct cc_channel * ch,
1601 unsigned int c1,
1602 unsigned int c2)
1603{
1604 unsigned int row;
1605
1606 /* PAC Preamble Address Codes -- 001 crrr 1ri xxxu */
1607
1608 row = cc_pac_row_map[(c1 & 7) * 2 + ((c2 >> 5) & 1)];
1609 if ((int) row < 0)
1610 return;
1611
1612 switch (ch->mode) {
1613 case CC_MODE_UNKNOWN:
1614 return;
1615
1616 case CC_MODE_ROLL_UP:
1617 /* EIA 608-B Annex C.4. */
1618 if (ch->window_rows > row + 1)
1619 row = ch->window_rows - 1;
1620
1621 /* 47 CFR Section 15.119 (f)(1)(ii). */
1622 /* May send a display event. */
1623 cc_move_window (cd, ch, row);
1624
1625 ch->curr_row = row;
1626
1627 break;
1628
1629 case CC_MODE_PAINT_ON:
1630 cc_stream_event_if_changed (cd, ch);
1631
1632 /* fall through */
1633
1634 case CC_MODE_POP_ON:
1635 /* XXX 47 CFR 15.119 (f)(2)(i), (f)(3)(i): In Pop-on
1636 and paint-on mode "Preamble Address Codes can be
1637 used to move the cursor around the screen in random
1638 order to place captions on Rows 1 to 15." We do not
1639 have a limit on the number of displayable rows, but
1640 as EIA 608-B Annex C.6 points out, if more than
1641 four rows must be displayed they were probably
1642 received in error and we should respond
1643 accordingly. */
1644
1645 /* 47 CFR Section 15.119 (d)(1)(i) and EIA 608-B Annex
1646 C.7. */
1647 ch->curr_row = row;
1648
1649 break;
1650
1651 case CC_MODE_TEXT:
1652 /* 47 CFR 15.119 (e)(1) and EIA 608-B Section 7.4:
1653 Does not change the cursor row. */
1654 break;
1655 }
1656
1657 if (c2 & 0x10) {
1658 /* 47 CFR 15.119 (e)(1)(i) and EIA 608-B Table 71. */
1659 ch->curr_column = CC_FIRST_COLUMN + (c2 & 0x0E) * 2;
1660 }
1661
1662 /* PAC is a non-spacing attribute for the next character, see
1663 cc_put_char(). */
1664 ch->last_pac = 0x1000 | c2;
1665}
1666
1667static void
1668cc_control_code (struct cc_decoder * cd,
1669 unsigned int c1,
1670 unsigned int c2,
1671 enum field_num f)
1672{
1673 struct cc_channel *ch;
1674 unsigned int ch_num0;
1675
1676 /* Caption / text, field 1 / 2, primary / secondary channel. */
1677 ch_num0 = (((cd->curr_ch_num[f] - VBI_CAPTION_CC1) & 4)
1678 + f * 2
1679 + ((c1 >> 3) & 1));
1680
1681 /* Note ch is invalid if UNKNOWN_CC_CHANNEL ==
1682 cd->curr_ch_num[f]. */
1683 ch = &cd->channel[ch_num0];
1684
1685 if (c2 >= 0x40) {
1686 /* Preamble Address Codes -- 001 crrr 1ri xxxu */
1687 if (UNKNOWN_CC_CHANNEL != cd->curr_ch_num[f])
1688 cc_preamble_address_code (cd, ch, c1, c2);
1689 return;
1690 }
1691
1692 switch (c1 & 7) {
1693 case 0:
1694 if (UNKNOWN_CC_CHANNEL == cd->curr_ch_num[f]
1695 || CC_MODE_UNKNOWN == ch->mode)
1696 break;
1697
1698 if (c2 < 0x30) {
1699 /* Backgr. Attr. Codes -- 001 c000 010 xxxt */
1700 /* EIA 608-B Section 6.2. */
1701 cc_put_char (cd, ch, 0x1000 | c2,
1702 /* displayable */ FALSE,
1703 /* backspace */ TRUE);
1704 } else {
1705 /* Undefined. */
1706 }
1707
1708 break;
1709
1710 case 1:
1711 if (UNKNOWN_CC_CHANNEL == cd->curr_ch_num[f]
1712 || CC_MODE_UNKNOWN == ch->mode)
1713 break;
1714
1715 if (c2 < 0x30) {
1716 /* Mid-Row Codes -- 001 c001 010 xxxu */
1717 /* 47 CFR 15.119 (h)(1)(i): Spacing attribute. */
1718 cc_put_char (cd, ch, 0x1100 | c2,
1719 /* displayable */ FALSE,
1720 /* backspace */ FALSE);
1721 } else {
1722 /* Special Characters -- 001 c001 011 xxxx */
1723 if (0x39 == c2) {
1724 /* Transparent space. */
1725 cc_put_char (cd, ch, 0,
1726 /* displayable */ FALSE,
1727 /* backspace */ FALSE);
1728 } else {
1729 cc_put_char (cd, ch, 0x1100 | c2,
1730 /* displayable */ TRUE,
1731 /* backspace */ FALSE);
1732 }
1733 }
1734
1735 break;
1736
1737 case 2:
1738 case 3: /* Extended Character Set -- 001 c01x 01x xxxx */
1739 if (UNKNOWN_CC_CHANNEL == cd->curr_ch_num[f]
1740 || CC_MODE_UNKNOWN == ch->mode)
1741 break;
1742
1743 /* EIA 608-B Section 6.4.2. */
1744 cc_put_char (cd, ch, (c1 * 256 + c2) & 0x777F,
1745 /* displayable */ TRUE,
1746 /* backspace */ TRUE);
1747 break;
1748
1749 case 4:
1750 case 5:
1751 if (c2 < 0x30) {
1752 /* Misc. Control Codes -- 001 c10f 010 xxxx */
1753 cc_misc_control_code (cd, ch, c2, ch_num0, f);
1754 } else {
1755 /* Undefined. */
1756 }
1757
1758 break;
1759
1760 case 6: /* reserved */
1761 break;
1762
1763 case 7: /* Extended control codes -- 001 c111 01x xxxx */
1764 if (UNKNOWN_CC_CHANNEL == cd->curr_ch_num[f]
1765 || CC_MODE_UNKNOWN == ch->mode)
1766 break;
1767
1768 cc_ext_control_code (cd, ch, c2);
1769
1770 break;
1771 }
1772}
1773
1774static vbi_bool
1775cc_characters (struct cc_decoder * cd,
1776 struct cc_channel * ch,
1777 int c)
1778{
1779 if (0 == c) {
1780 if (CC_MODE_UNKNOWN == ch->mode)
1781 return TRUE;
1782
1783 /* XXX After x NUL characters (presumably a caption
1784 pause), force a display update if we do not send
1785 events on every display change. */
1786
1787 return TRUE;
1788 }
1789
1790 if (c < 0x20) {
1791 /* Parity error or invalid data. */
1792
1793 if (c < 0 && CC_MODE_UNKNOWN != ch->mode) {
1794 /* 47 CFR Section 15.119 (j)(1). */
1795 cc_put_char (cd, ch, 0x7F,
1796 /* displayable */ TRUE,
1797 /* backspace */ FALSE);
1798 }
1799
1800 return FALSE;
1801 }
1802
1803 if (CC_MODE_UNKNOWN != ch->mode) {
1804 cc_put_char (cd, ch, c,
1805 /* displayable */ TRUE,
1806 /* backspace */ FALSE);
1807 }
1808
1809 return TRUE;
1810}
1811
1812vbi_bool
1813cc_feed (struct cc_decoder * cd,
1814 const uint8_t buffer[2],
1815 unsigned int line,
1816 const struct timeval * tv,
1817 int64_t pts)
1818{
1819 int c1, c2;
1820 enum field_num f;
1821 vbi_bool all_successful;
1822
1823 assert (NULL != cd);
1824
1825 f = FIELD_1;
1826
1827 switch (line) {
1828 case 21: /* NTSC */
1829 case 22: /* PAL/SECAM */
1830 break;
1831
1832 case 284: /* NTSC */
1833 f = FIELD_2;
1834 break;
1835
1836 default:
1837 return FALSE;
1838 }
1839
1840 cd->timestamp.sys = *tv;
1841 cd->timestamp.pts = pts;
1842
1843 /* FIXME deferred reset here */
1844
1845 c1 = vbi_unpar8 (buffer[0]);
1846 c2 = vbi_unpar8 (buffer[1]);
1847
1848 all_successful = TRUE;
1849
1850 /* 47 CFR 15.119 (2)(i)(4): "If the first transmission of a
1851 control code pair passes parity, it is acted upon within
1852 one video frame. If the next frame contains a perfect
1853 repeat of the same pair, the redundant code is ignored. If,
1854 however, the next frame contains a different but also valid
1855 control code pair, this pair, too, will be acted upon (and
1856 the receiver will expect a repeat of this second pair in
1857 the next frame). If the first byte of the expected
1858 redundant control code pair fails the parity check and the
1859 second byte is identical to the second byte in the
1860 immediately preceding pair, then the expected redundant
1861 code is ignored. If there are printing characters in place
1862 of the redundant code, they will be processed normally."
1863
1864 EIA 608-B Section 8.3: Caption control codes on field 2 may
1865 repeat as on field 1. Section 8.6.2: XDS control codes
1866 shall not repeat. */
1867
1868 if (unlikely (c1 < 0)) {
1869 goto parity_error;
1870 } else if (c1 == cd->expect_ctrl[f][0]
1871 && c2 == cd->expect_ctrl[f][1]) {
1872 /* Already acted upon. */
1873 cd->expect_ctrl[f][0] = -1;
1874 goto finish;
1875 }
1876
1877 if (c1 >= 0x10 && c1 < 0x20) {
1878 /* Caption control code. */
1879
1880 /* There's no XDS on field 1, we just
1881 use an array to save a branch. */
1882 cd->in_xds[f] = FALSE;
1883
1884 /* 47 CFR Section 15.119 (i)(1), (i)(2). */
1885 if (c2 < 0x20) {
1886 /* Parity error or invalid control code.
1887 Let's hope it repeats. */
1888 goto parity_error;
1889 }
1890
1891 cc_control_code (cd, c1, c2, f);
1892
1893 if (cd->event_pending) {
1894 cc_display_event (cd, cd->event_pending, 0);
1895 cd->event_pending = NULL;
1896 }
1897
1898 cd->expect_ctrl[f][0] = c1;
1899 cd->expect_ctrl[f][1] = c2;
1900 } else {
1901 cd->expect_ctrl[f][0] = -1;
1902
1903 if (c1 < 0x10) {
1904 if (FIELD_1 == f) {
1905 /* 47 CFR Section 15.119 (i)(1): "If the
1906 non-printing character in the pair is
1907 in the range 00h to 0Fh, that character
1908 alone will be ignored and the second
1909 character will be treated normally." */
1910 c1 = 0;
1911 } else if (0x0F == c1) {
1912 /* XDS packet terminator. */
1913 cd->in_xds[FIELD_2] = FALSE;
1914 goto finish;
1915 } else if (c1 >= 0x01) {
1916 /* XDS packet start or continuation.
1917 EIA 608-B Section 7.7, 8.5: Also
1918 interrupts a Text mode
1919 transmission. */
1920 cd->in_xds[FIELD_2] = TRUE;
1921 goto finish;
1922 }
1923 }
1924
1925 {
1926 struct cc_channel *ch;
1927 vbi_pgno ch_num;
1928
1929 ch_num = cd->curr_ch_num[f];
1930 if (UNKNOWN_CC_CHANNEL == ch_num)
1931 goto finish;
1932
1933 ch_num = ((ch_num - VBI_CAPTION_CC1) & 5) + f * 2;
1934 ch = &cd->channel[ch_num];
1935
1936 all_successful &= cc_characters (cd, ch, c1);
1937 all_successful &= cc_characters (cd, ch, c2);
1938
1939 if (cd->event_pending) {
1940 cc_display_event (cd, cd->event_pending, 0);
1941 cd->event_pending = NULL;
1942 }
1943 }
1944 }
1945
1946 finish:
1947 cd->error_history = cd->error_history * 2 + all_successful;
1948
1949 return all_successful;
1950
1951 parity_error:
1952 cd->expect_ctrl[f][0] = -1;
1953
1954 /* XXX Some networks stupidly transmit 0x0000 instead of
1955 0x8080 as filler. Perhaps we shouldn't take that as a
1956 serious parity error. */
1957 cd->error_history *= 2;
1958
1959 return FALSE;
1960}
1961
1962void
1963cc_reset (struct cc_decoder * cd)
1964{
1965 unsigned int ch_num;
1966
1967 assert (NULL != cd);
1968
1969 for (ch_num = 0; ch_num < MAX_CC_CHANNELS; ++ch_num) {
1970 struct cc_channel *ch;
1971
1972 ch = &cd->channel[ch_num];
1973
1974 if (ch_num <= 3) {
1975 ch->mode = CC_MODE_UNKNOWN;
1976
1977 /* Something suitable for roll-up mode. */
1978 ch->curr_row = CC_LAST_ROW;
1979 ch->curr_column = CC_FIRST_COLUMN;
1980 ch->window_rows = 4;
1981 } else {
1982 ch->mode = CC_MODE_TEXT; /* invariable */
1983
1984 /* EIA 608-B Section 7.4: "When Text Mode has
1985 initially been selected and the specified
1986 Text memory is empty, the cursor starts at
1987 the topmost row, Column 1." */
1988 ch->curr_row = CC_FIRST_ROW;
1989 ch->curr_column = CC_FIRST_COLUMN;
1990 ch->window_rows = 0; /* n/a */
1991 }
1992
1993 ch->displayed_buffer = 0;
1994
1995 ch->last_pac = 0;
1996
1997 CLEAR (ch->buffer);
1998 CLEAR (ch->dirty);
1999
2000 cc_timestamp_reset (&ch->timestamp);
2001 cc_timestamp_reset (&ch->timestamp_c0);
2002 }
2003
2004 cd->curr_ch_num[0] = UNKNOWN_CC_CHANNEL;
2005 cd->curr_ch_num[1] = UNKNOWN_CC_CHANNEL;
2006
2007 memset (cd->expect_ctrl, -1, sizeof (cd->expect_ctrl));
2008
2009 CLEAR (cd->in_xds);
2010
2011 cd->event_pending = NULL;
2012}
2013
2014void
2015cc_init (struct cc_decoder * cd)
2016{
2017 cc_reset (cd);
2018
2019 cd->error_history = 0;
2020
2021 cc_timestamp_reset (&cd->timestamp);
2022}
2023
2024#if 0 /* to be replaced */
2025
2026static int webtv_check(struct caption_recorder *cr, char * buf,int len)
2027{
2028 unsigned long sum;
2029 unsigned long nwords;
2030 unsigned short csum=0;
2031 char temp[9];
2032 int nbytes=0;
2033
2034 while (buf[0]!='<' && len > 6) //search for the start
2035 {
2036 buf++; len--;
2037 }
2038
2039 if (len == 6) //failure to find start
2040 return 0;
2041
2042
2043 while (nbytes+6 <= len)
2044 {
2045 //look for end of object checksum, it's enclosed in []'s and there shouldn't be any [' after
2046 if (buf[nbytes] == '[' && buf[nbytes+5] == ']' && buf[nbytes+6] != '[')
2047 break;
2048 else
2049 nbytes++;
2050 }
2051 if (nbytes+6>len) //failure to find end
2052 return 0;
2053
2054 nwords = nbytes >> 1; sum = 0;
2055
2056 //add up all two byte words
2057 while (nwords-- > 0) {
2058 sum += *buf++ << 8;
2059 sum += *buf++;
2060 }
2061 if (nbytes & 1) {
2062 sum += *buf << 8;
2063 }
2064 csum = (unsigned short)(sum >> 16);
2065 while(csum !=0) {
2066 sum = csum + (sum & 0xffff);
2067 csum = (unsigned short)(sum >> 16);
2068 }
2069 sprintf(temp,"%04X\n",(int)~sum&0xffff);
2070 buf++;
2071 if(!strncmp(buf,temp,4))
2072 {
2073 buf[5]=0;
2074 if (cr->cur_ch[cr->field] >= 0 && cr->cc_fp[cr->cur_ch[cr->field]]) {
2075 if (!cr->plain)
2076 fprintf(cr->cc_fp[cr->cur_ch[cr->field]], "\33[35mWEBTV: %s\33[0m\n",buf-nbytes-1);
2077 else
2078 fprintf(cr->cc_fp[cr->cur_ch[cr->field]], "WEBTV: %s\n",buf-nbytes-1);
2079 fflush (cr->cc_fp[cr->cur_ch[cr->field]]);
2080 }
2081 }
2082 return 0;
2083}
2084
2085#endif /* 0 */
2086
2087/* CEA 708-C Digital TV Closed Caption decoder. */
2088
2089static const uint8_t
2090dtvcc_c0_length [4] = {
2091 1, 1, 2, 3
2092};
2093
2094static const uint8_t
2095dtvcc_c1_length [32] = {
2096 /* 0x80 CW0 ... CW7 */ 1, 1, 1, 1, 1, 1, 1, 1,
2097 /* 0x88 CLW */ 2,
2098 /* 0x89 DSW */ 2,
2099 /* 0x8A HDW */ 2,
2100 /* 0x8B TGW */ 2,
2101
2102 /* 0x8C DLW */ 2,
2103 /* 0x8D DLY */ 2,
2104 /* 0x8E DLC */ 1,
2105 /* 0x8F RST */ 1,
2106
2107 /* 0x90 SPA */ 3,
2108 /* 0x91 SPC */ 4,
2109 /* 0x92 SPL */ 3,
2110 /* CEA 708-C Section 7.1.5.1: 0x93 ... 0x96 are
2111 reserved one byte codes. */ 1, 1, 1, 1,
2112 /* 0x97 SWA */ 5,
2113 /* 0x98 DF0 ... DF7 */ 7, 7, 7, 7, 7, 7, 7, 7
2114};
2115
2116static const uint16_t
2117dtvcc_g2 [96] = {
2118 /* Note Unicode defines no transparent spaces. */
2119 0x0020, /* 0x1020 Transparent space */
2120 0x00A0, /* 0x1021 Non-breaking transparent space */
2121
2122 0, /* 0x1022 reserved */
2123 0,
2124 0,
2125 0x2026, /* 0x1025 Horizontal ellipsis */
2126 0,
2127 0,
2128 0,
2129 0,
2130 0x0160, /* 0x102A S with caron */
2131 0,
2132 0x0152, /* 0x102C Ligature OE */
2133 0,
2134 0,
2135 0,
2136
2137 /* CEA 708-C Section 7.1.8: "The character (0x30) is a solid
2138 block which fills the entire character position with the
2139 text foreground color." */
2140 0x2588, /* 0x1030 Full block */
2141
2142 0x2018, /* 0x1031 Left single quotation mark */
2143 0x2019, /* 0x1032 Right single quotation mark */
2144 0x201C, /* 0x1033 Left double quotation mark */
2145 0x201D, /* 0x1034 Right double quotation mark */
2146 0,
2147 0,
2148 0,
2149 0x2122, /* 0x1039 Trademark sign */
2150 0x0161, /* 0x103A s with caron */
2151 0,
2152 0x0153, /* 0x103C Ligature oe */
2153 0x2120, /* 0x103D Service mark */
2154 0,
2155 0x0178, /* 0x103F Y with diaeresis */
2156
2157 /* Code points 0x1040 ... 0x106F reserved. */
2158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2161
2162 0, /* 0x1070 reserved */
2163 0,
2164 0,
2165 0,
2166 0,
2167 0,
2168 0x215B, /* 0x1076 1/8 */
2169 0x215C, /* 0x1077 3/8 */
2170 0x215D, /* 0x1078 5/8 */
2171 0x215E, /* 0x1079 7/8 */
2172 0x2502, /* 0x107A Box drawings vertical */
2173 0x2510, /* 0x107B Box drawings down and left */
2174 0x2514, /* 0x107C Box drawings up and right */
2175 0x2500, /* 0x107D Box drawings horizontal */
2176 0x2518, /* 0x107E Box drawings up and left */
2177 0x250C /* 0x107F Box drawings down and right */
2178};
2179
2180static vbi_rgba
2181dtvcc_color_map[8] = {
2182 VBI_RGBA(0x00, 0x00, 0x00), VBI_RGBA(0xFF, 0x00, 0x00),
2183 VBI_RGBA(0x00, 0xFF, 0x00), VBI_RGBA(0xFF, 0xFF, 0x00),
2184 VBI_RGBA(0x00, 0x00, 0xFF), VBI_RGBA(0xFF, 0x00, 0xFF),
2185 VBI_RGBA(0x00, 0xFF, 0xFF), VBI_RGBA(0xFF, 0xFF, 0xFF)
2186};
2187
2188
2189static void
2190dtvcc_set_page_color_map(vbi_decoder *vbi, vbi_page *pg)
2191{
2192 vbi_transp_colormap(vbi, pg->color_map, dtvcc_color_map, 8);
2193}
2194
2195static vbi_color
2196dtvcc_map_color(dtvcc_color c)
2197{
2198 vbi_color ret = VBI_BLACK;
2199
2200 c &= 0x2A;
2201 switch(c){
2202 case 0:
2203 ret = VBI_BLACK;
2204 break;
2205 case 0x20:
2206 ret = VBI_RED;
2207 break;
2208 case 0x08:
2209 ret = VBI_GREEN;
2210 break;
2211 case 0x28:
2212 ret = VBI_YELLOW;
2213 break;
2214 case 0x02:
2215 ret = VBI_BLUE;
2216 break;
2217 case 0x22:
2218 ret = VBI_MAGENTA;
2219 break;
2220 case 0x0A:
2221 ret = VBI_CYAN;
2222 break;
2223 case 0x2A:
2224 ret = VBI_WHITE;
2225 break;
2226 }
2227
2228 return ret;
2229}
2230
2231static unsigned int
2232dtvcc_unicode (unsigned int c)
2233{
2234 if (unlikely (0 == (c & 0x60))) {
2235 /* C0, C1, C2, C3 */
2236 return 0;
2237 } else if (likely (c < 0x100)) {
2238 /* G0, G1 */
2239 if (unlikely (0x7F == c))
2240 return 0x266A; /* music note */
2241 else
2242 return c;
2243 } else if (c < 0x1080) {
2244 if (unlikely (c < 0x1020))
2245 return 0;
2246 else
2247 return dtvcc_g2[c - 0x1020];
2248 } else if (0x10A0 == c) {
2249 /* We map all G2/G3 characters which are not
2250 representable in Unicode to private code U+E900
2251 ... U+E9FF. */
2252 return 0xE9A0; /* caption icon */
2253 }
2254
2255 return 0;
2256}
2257
2258static void
2259dtvcc_render(struct dtvcc_decoder * dc, struct dtvcc_service * ds)
2260{
2261 vbi_event event;
2262 struct tvcc_decoder *td = PARENT(dc, struct tvcc_decoder, dtvcc);
2263
2264 event.type = VBI_EVENT_CAPTION;
2265 event.ev.caption.pgno = ds - dc->service + 1 + 8/*after 8 cc channels*/;
2266
2267 /* Permits calling tvcc_fetch_page from handler */
2268 pthread_mutex_unlock(&td->mutex);
2269
2270 vbi_send_event(td->vbi, &event);
2271
2272 pthread_mutex_lock(&td->mutex);
2273}
2274
2275static void
2276dtvcc_reset_service (struct dtvcc_service * ds);
2277
2278static unsigned int
2279dtvcc_window_id (struct dtvcc_service * ds,
2280 struct dtvcc_window * dw)
2281{
2282 return dw - ds->window;
2283}
2284
2285static unsigned int
2286dtvcc_service_num (struct dtvcc_decoder * dc,
2287 struct dtvcc_service * ds)
2288{
2289 return ds - dc->service + 1;
2290}
2291
2292/* Up to eight windows can be visible at once, so which one displays
2293 the caption? Let's take a guess. */
2294static struct dtvcc_window *
2295dtvcc_caption_window (struct dtvcc_service * ds)
2296{
2297 struct dtvcc_window *dw;
2298 unsigned int max_priority;
2299 unsigned int window_id;
2300
2301 dw = NULL;
2302 max_priority = 8;
2303
2304 for (window_id = 0; window_id < 8; ++window_id) {
2305 if (0 == (ds->created & (1 << window_id)))
2306 continue;
2307 if (!ds->window[window_id].visible)
2308 continue;
2309 if (DIR_BOTTOM_TOP
2310 != ds->window[window_id].style.scroll_direction)
2311 continue;
2312 if (ds->window[window_id].priority < max_priority) {
2313 dw = &ds->window[window_id];
2314 max_priority = ds->window[window_id].priority;
2315 }
2316 }
2317
2318 return dw;
2319}
2320
2321static void
2322dtvcc_stream_event (struct dtvcc_decoder * dc,
2323 struct dtvcc_service * ds,
2324 struct dtvcc_window * dw,
2325 unsigned int row)
2326{
2327 vbi_char text[48];
2328 vbi_char ac;
2329 unsigned int column;
2330
2331 if (NULL == dw || dw != dtvcc_caption_window (ds))
2332 return;
2333
2334 /* Note we only stream windows with scroll direction
2335 upwards. */
2336 if (0 != (dw->streamed & (1 << row))
2337 /* || !cc_timestamp_isset (&dw->timestamp_c0)*/)
2338 return;
2339
2340 dw->streamed |= 1 << row;
2341
2342 for (column = 0; column < dw->column_count; ++column) {
2343 if (0 != dw->buffer[row][column])
2344 break;
2345 }
2346
2347 /* Row contains only transparent spaces. */
2348 if (column >= dw->column_count)
2349 return;
2350
2351
2352 dtvcc_render(dc, ds);
2353
2354 /* TO DO. */
2355 CLEAR (ac);
2356 ac.foreground = VBI_WHITE;
2357 ac.background = VBI_BLACK;
2358 ac.opacity = VBI_OPAQUE;
2359
2360 for (column = 0; column < dw->column_count; ++column) {
2361 unsigned int c;
2362
2363 c = dw->buffer[row][column];
2364 if (0 == c) {
2365 ac.unicode = 0x20;
2366 } else {
2367 ac.unicode = dtvcc_unicode (c);
2368 if (0 == ac.unicode) {
2369 ac.unicode = 0x20;
2370 }
2371 }
2372 text[column] = ac;
2373 }
2374
2375 cc_timestamp_reset (&dw->timestamp_c0);
2376}
2377
2378static vbi_bool
2379dtvcc_put_char (struct dtvcc_decoder * dc,
2380 struct dtvcc_service * ds,
2381 unsigned int c)
2382{
2383 struct dtvcc_window *dw;
2384 unsigned int row;
2385 unsigned int column;
2386
2387 dc = dc; /* unused */
2388
2389 dw = ds->curr_window;
2390 if (NULL == dw) {
2391 ds->error_line = __LINE__;
2392 return FALSE;
2393 }
2394
2395 row = dw->curr_row;
2396 column = dw->curr_column;
2397
2398 /* FIXME how should we handle TEXT_TAG_NOT_DISPLAYABLE? */
2399
2400 dw->buffer[row][column] = c;
2401
2402 switch (dw->style.print_direction) {
2403 case DIR_LEFT_RIGHT:
2404 dw->streamed &= ~(1 << row);
2405 if (!cc_timestamp_isset (&dw->timestamp_c0))
2406 dw->timestamp_c0 = ds->timestamp;
2407 if (++column >= dw->column_count)
2408 return TRUE;
2409 break;
2410
2411 case DIR_RIGHT_LEFT:
2412 dw->streamed &= ~(1 << row);
2413 if (!cc_timestamp_isset (&dw->timestamp_c0))
2414 dw->timestamp_c0 = ds->timestamp;
2415 if (column-- <= 0)
2416 return TRUE;
2417 break;
2418
2419 case DIR_TOP_BOTTOM:
2420 dw->streamed &= ~(1 << column);
2421 if (!cc_timestamp_isset (&dw->timestamp_c0))
2422 dw->timestamp_c0 = ds->timestamp;
2423 if (++row >= dw->row_count)
2424 return TRUE;
2425 break;
2426
2427 case DIR_BOTTOM_TOP:
2428 dw->streamed &= ~(1 << column);
2429 if (!cc_timestamp_isset (&dw->timestamp_c0))
2430 dw->timestamp_c0 = ds->timestamp;
2431 if (row-- <= 0)
2432 return TRUE;
2433 break;
2434 }
2435
2436 dw->curr_row = row;
2437 dw->curr_column = column;
2438
2439 dtvcc_render(dc, ds);
2440
2441 return TRUE;
2442}
2443
2444static vbi_bool
2445dtvcc_set_pen_location (struct dtvcc_decoder * dc,
2446 struct dtvcc_service * ds,
2447 const uint8_t * buf)
2448{
2449 struct dtvcc_window *dw;
2450 unsigned int row;
2451 unsigned int column;
2452
2453 dw = ds->curr_window;
2454 if (NULL == dw) {
2455 ds->error_line = __LINE__;
2456 return FALSE;
2457 }
2458
2459 row = buf[1];
2460 /* We check the top four zero bits. */
2461 if (row >= 16) {
2462 ds->error_line = __LINE__;
2463 return FALSE;
2464 }
2465
2466 column = buf[2];
2467 /* We also check the top two zero bits. */
2468 if (column >= 42) {
2469 ds->error_line = __LINE__;
2470 return FALSE;
2471 }
2472
2473 if (row > dw->row_count)
2474 row = dw->row_count - 1;
2475 if (column > dw->column_count)
2476 column = dw->column_count - 1;
2477
2478 if (row != dw->curr_row) {
2479 dtvcc_stream_event (dc, ds, dw, dw->curr_row);
2480 }
2481
2482 /* FIXME there's more. */
2483 dw->curr_row = row;
2484 dw->curr_column = column;
2485
2486 return TRUE;
2487}
2488
2489static vbi_bool
2490dtvcc_set_pen_color (struct dtvcc_service * ds,
2491 const uint8_t * buf)
2492{
2493 struct dtvcc_window *dw;
2494 unsigned int c;
2495
2496 dw = ds->curr_window;
2497 if (NULL == dw) {
2498 ds->error_line = __LINE__;
2499 return FALSE;
2500 }
2501
2502 c = buf[3];
2503 if (0 != (c & 0xC0)) {
2504 ds->error_line = __LINE__;
2505 return FALSE;
2506 }
2507
2508 dw->curr_pen.style.edge_color = c;
2509 c = buf[1];
2510 dw->curr_pen.style.fg_opacity = c >> 6;
2511 dw->curr_pen.style.fg_color = c & 0x3F;
2512 c = buf[2];
2513 dw->curr_pen.style.bg_opacity = c >> 6;
2514 dw->curr_pen.style.bg_color = c & 0x3F;
2515
2516 return TRUE;
2517}
2518
2519static vbi_bool
2520dtvcc_set_pen_attributes (struct dtvcc_service * ds,
2521 const uint8_t * buf)
2522{
2523 struct dtvcc_window *dw;
2524 unsigned int c;
2525 enum pen_size pen_size;
2526 enum offset offset;
2527 enum edge edge_type;
2528
2529 dw = ds->curr_window;
2530 if (NULL == dw) {
2531 ds->error_line = __LINE__;
2532 return FALSE;
2533 }
2534
2535 c = buf[1];
2536 offset = (c >> 2) & 3;
2537 pen_size = c & 3;
2538 if ((offset | pen_size) >= 3) {
2539 ds->error_line = __LINE__;
2540 return FALSE;
2541 }
2542
2543 c = buf[2];
2544 edge_type = (c >> 3) & 7;
2545 if (edge_type >= 6) {
2546 ds->error_line = __LINE__;
2547 return FALSE;
2548 }
2549
2550 c = buf[1];
2551 dw->curr_pen.text_tag = c >> 4;
2552 dw->curr_pen.style.offset = offset;
2553 dw->curr_pen.style.pen_size = pen_size;
2554 c = buf[2];
2555 dw->curr_pen.style.italics = c >> 7;
2556 dw->curr_pen.style.underline = (c >> 6) & 1;
2557 dw->curr_pen.style.edge_type = edge_type;
2558 dw->curr_pen.style.font_style = c & 7;
2559
2560 return TRUE;
2561}
2562
2563static vbi_bool
2564dtvcc_set_window_attributes (struct dtvcc_service * ds,
2565 const uint8_t * buf)
2566{
2567 struct dtvcc_window *dw;
2568 unsigned int c;
2569 enum edge border_type;
2570 enum display_effect display_effect;
2571
2572 dw = ds->curr_window;
2573 if (NULL == dw)
2574 return FALSE;
2575
2576 c = buf[2];
2577 border_type = ((buf[3] >> 5) & 0x04) | (c >> 6);
2578 if (border_type >= 6)
2579 return FALSE;
2580
2581 c = buf[4];
2582 display_effect = c & 3;
2583 if (display_effect >= 3)
2584 return FALSE;
2585
2586 c = buf[1];
2587 dw->style.fill_opacity = c >> 6;
2588 dw->style.fill_color = c & 0x3F;
2589 c = buf[2];
2590 dw->style.border_type = border_type;
2591 dw->style.border_color = c & 0x3F;
2592 c = buf[3];
2593 dw->style.wordwrap = (c >> 6) & 1;
2594 dw->style.print_direction = (c >> 4) & 3;
2595 dw->style.scroll_direction = (c >> 2) & 3;
2596 dw->style.justify = c & 3;
2597 c = buf[4];
2598 dw->style.effect_speed = c >> 4;
2599 dw->style.effect_direction = (c >> 2) & 3;
2600 dw->style.display_effect = display_effect;
2601
2602 return TRUE;
2603}
2604
2605static vbi_bool
2606dtvcc_clear_windows (struct dtvcc_decoder * dc,
2607 struct dtvcc_service * ds,
2608 dtvcc_window_map window_map)
2609{
2610 unsigned int i;
2611
2612 window_map &= ds->created;
2613
2614 for (i = 0; i < 8; ++i) {
2615 struct dtvcc_window *dw;
2616
2617 if (0 == (window_map & (1 << i)))
2618 continue;
2619
2620 dw = &ds->window[i];
2621
2622 dtvcc_stream_event (dc, ds, dw, dw->curr_row);
2623
2624 memset (dw->buffer, 0, sizeof (dw->buffer));
2625
2626 dw->streamed = 0;
2627
2628 /* FIXME CEA 708-C Section 7.1.4 (Form Feed)
2629 and 8.10.5.3 confuse me. */
2630 if (0) {
2631 dw->curr_column = 0;
2632 dw->curr_row = 0;
2633 }
2634 }
2635
2636 return TRUE;
2637}
2638
2639static vbi_bool
2640dtvcc_define_window (struct dtvcc_decoder * dc,
2641 struct dtvcc_service * ds,
2642 uint8_t * buf)
2643{
2644 static const struct dtvcc_window_style window_styles [7] = {
2645 {
2646 JUSTIFY_LEFT, DIR_LEFT_RIGHT, DIR_BOTTOM_TOP,
2647 FALSE, DISPLAY_EFFECT_SNAP, 0, 0, 0,
2648 OPACITY_SOLID, EDGE_NONE, 0
2649 }, {
2650 JUSTIFY_LEFT, DIR_LEFT_RIGHT, DIR_BOTTOM_TOP,
2651 FALSE, DISPLAY_EFFECT_SNAP, 0, 0, 0,
2652 OPACITY_TRANSPARENT, EDGE_NONE, 0
2653 }, {
2654 JUSTIFY_CENTER, DIR_LEFT_RIGHT, DIR_BOTTOM_TOP,
2655 FALSE, DISPLAY_EFFECT_SNAP, 0, 0, 0,
2656 OPACITY_SOLID, EDGE_NONE, 0
2657 }, {
2658 JUSTIFY_LEFT, DIR_LEFT_RIGHT, DIR_BOTTOM_TOP,
2659 TRUE, DISPLAY_EFFECT_SNAP, 0, 0, 0,
2660 OPACITY_SOLID, EDGE_NONE, 0
2661 }, {
2662 JUSTIFY_LEFT, DIR_LEFT_RIGHT, DIR_BOTTOM_TOP,
2663 TRUE, DISPLAY_EFFECT_SNAP, 0, 0, 0,
2664 OPACITY_TRANSPARENT, EDGE_NONE, 0
2665 }, {
2666 JUSTIFY_CENTER, DIR_LEFT_RIGHT, DIR_BOTTOM_TOP,
2667 TRUE, DISPLAY_EFFECT_SNAP, 0, 0, 0,
2668 OPACITY_SOLID, EDGE_NONE, 0
2669 }, {
2670 JUSTIFY_LEFT, DIR_TOP_BOTTOM, DIR_RIGHT_LEFT,
2671 FALSE, DISPLAY_EFFECT_SNAP, 0, 0, 0,
2672 OPACITY_SOLID, EDGE_NONE, 0
2673 }
2674 };
2675 static const struct dtvcc_pen_style pen_styles [7] = {
2676 {
2677 PEN_SIZE_STANDARD, 0, OFFSET_NORMAL, FALSE,
2678 FALSE, EDGE_NONE, 0x3F, OPACITY_SOLID,
2679 0x00, OPACITY_SOLID, 0
2680 }, {
2681 PEN_SIZE_STANDARD, 1, OFFSET_NORMAL, FALSE,
2682 FALSE, EDGE_NONE, 0x3F, OPACITY_SOLID,
2683 0x00, OPACITY_SOLID, 0
2684 }, {
2685 PEN_SIZE_STANDARD, 2, OFFSET_NORMAL, FALSE,
2686 FALSE, EDGE_NONE, 0x3F, OPACITY_SOLID,
2687 0x00, OPACITY_SOLID, 0
2688 }, {
2689 PEN_SIZE_STANDARD, 3, OFFSET_NORMAL, FALSE,
2690 FALSE, EDGE_NONE, 0x3F, OPACITY_SOLID,
2691 0x00, OPACITY_SOLID, 0
2692 }, {
2693 PEN_SIZE_STANDARD, 4, OFFSET_NORMAL, FALSE,
2694 FALSE, EDGE_NONE, 0x3F, OPACITY_SOLID,
2695 0x00, OPACITY_SOLID, 0
2696 }, {
2697 PEN_SIZE_STANDARD, 3, OFFSET_NORMAL, FALSE,
2698 FALSE, EDGE_UNIFORM, 0x3F, OPACITY_SOLID,
2699 0, OPACITY_TRANSPARENT, 0x00
2700 }, {
2701 PEN_SIZE_STANDARD, 4, OFFSET_NORMAL, FALSE,
2702 FALSE, EDGE_UNIFORM, 0x3F, OPACITY_SOLID,
2703 0, OPACITY_TRANSPARENT, 0x00
2704 }
2705 };
2706 struct dtvcc_window *dw;
2707 dtvcc_window_map window_map;
2708 vbi_bool anchor_relative;
2709 unsigned int anchor_vertical;
2710 unsigned int anchor_horizontal;
2711 unsigned int anchor_point;
2712 unsigned int column_count_m1;
2713 unsigned int window_id;
2714 unsigned int window_style_id;
2715 unsigned int pen_style_id;
2716 unsigned int c;
2717
2718 if (0 != ((buf[1] | buf[6]) & 0xC0)) {
2719 ds->error_line = __LINE__;
2720 return FALSE;
2721 }
2722
2723 c = buf[2];
2724 anchor_relative = (c >> 7) & 1;
2725 anchor_vertical = c & 0x7F;
2726 anchor_horizontal = buf[3];
2727 if (0 == anchor_relative) {
2728 if (unlikely (anchor_vertical >= 75
2729 || anchor_horizontal >= 210)) {
2730 ds->error_line = __LINE__;
2731 return FALSE;
2732 }
2733 } else {
2734 if (unlikely (anchor_vertical >= 100
2735 || anchor_horizontal >= 100)) {
2736 ds->error_line = __LINE__;
2737 return FALSE;
2738 }
2739 }
2740
2741 c = buf[4];
2742 anchor_point = c >> 4;
2743 if (unlikely (anchor_point >= 9)) {
2744 ds->error_line = __LINE__;
2745 return FALSE;
2746 }
2747
2748 column_count_m1 = buf[5];
2749 /* We also check the top two zero bits. */
2750 if (unlikely (column_count_m1 >= 41)) {
2751 ds->error_line = __LINE__;
2752 return FALSE;
2753 }
2754
2755 window_id = buf[0] & 7;
2756 dw = &ds->window[window_id];
2757 window_map = 1 << window_id;
2758
2759 ds->curr_window = dw;
2760
2761 c = buf[1];
2762 dw->visible = (c >> 5) & 1;
2763 dw->row_lock = (c >> 4) & 1;
2764 dw->column_lock = (c >> 4) & 1;
2765 dw->priority = c & 7;
2766
2767 dw->anchor_relative = anchor_relative;
2768 dw->anchor_vertical = anchor_vertical;
2769 dw->anchor_horizontal = anchor_horizontal;
2770 dw->anchor_point = anchor_point;
2771
2772 c = buf[4];
2773 dw->row_count = (c & 15) + 1;
2774 dw->column_count = column_count_m1 + 1;
2775
2776 c = buf[6];
2777 window_style_id = (c >> 3) & 7;
2778 pen_style_id = c & 7;
2779
2780 if (window_style_id > 0) {
2781 dw->style = window_styles[window_style_id];
2782 } else if (0 == (ds->created & window_map)) {
2783 dw->style = window_styles[1];
2784 }
2785
2786 if (pen_style_id > 0) {
2787 dw->curr_pen.style = pen_styles[pen_style_id];
2788 } else if (0 == (ds->created & window_map)) {
2789 dw->curr_pen.style = pen_styles[1];
2790 }
2791
2792 if (0 != (ds->created & window_map))
2793 return TRUE;
2794
2795 /* Has to be something, no? */
2796 dw->curr_pen.text_tag = TEXT_TAG_NOT_DISPLAYABLE;
2797
2798 dw->curr_column = 0;
2799 dw->curr_row = 0;
2800
2801 dw->streamed = 0;
2802
2803 cc_timestamp_reset (&dw->timestamp_c0);
2804
2805 ds->created |= window_map;
2806
2807 return dtvcc_clear_windows (dc, ds, window_map);
2808}
2809
2810static vbi_bool
2811dtvcc_display_windows (struct dtvcc_decoder * dc,
2812 struct dtvcc_service * ds,
2813 unsigned int c,
2814 dtvcc_window_map window_map)
2815{
2816 unsigned int i;
2817
2818 window_map &= ds->created;
2819
2820 for (i = 0; i < 8; ++i) {
2821 struct dtvcc_window *dw;
2822 vbi_bool was_visible;
2823
2824 if (0 == (window_map & (1 << i)))
2825 continue;
2826
2827 dw = &ds->window[i];
2828 was_visible = dw->visible;
2829
2830 switch (c) {
2831 case 0x89: /* DSW DisplayWindows */
2832 dw->visible = TRUE;
2833 break;
2834
2835 case 0x8A: /* HDW HideWindows */
2836 dw->visible = FALSE;
2837 break;
2838
2839 case 0x8B: /* TGW ToggleWindows */
2840 dw->visible = was_visible ^ TRUE;
2841 break;
2842 }
2843
2844 if (!was_visible) {
2845 unsigned int row;
2846
2847 dw->timestamp_c0 = ds->timestamp;
2848 for (row = 0; row < dw->row_count; ++row) {
2849 dtvcc_stream_event (dc, ds, dw, row);
2850 }
2851 }
2852 }
2853
2854 return TRUE;
2855}
2856
2857static vbi_bool
2858dtvcc_carriage_return (struct dtvcc_decoder * dc,
2859 struct dtvcc_service * ds)
2860{
2861 struct dtvcc_window *dw;
2862 unsigned int row;
2863 unsigned int column;
2864
2865 dw = ds->curr_window;
2866 if (NULL == dw) {
2867 ds->error_line = __LINE__;
2868 return FALSE;
2869 }
2870
2871 dtvcc_stream_event (dc, ds, dw, dw->curr_row);
2872
2873 row = dw->curr_row;
2874 column = dw->curr_column;
2875
2876 switch (dw->style.scroll_direction) {
2877 case DIR_LEFT_RIGHT:
2878 dw->curr_row = 0;
2879 if (column > 0) {
2880 dw->curr_column = column - 1;
2881 break;
2882 }
2883 dw->streamed = (dw->streamed << 1)
2884 & ~(1 << dw->column_count);
2885 for (row = 0; row < dw->row_count; ++row) {
2886 for (column = dw->column_count - 1;
2887 column > 0; --column) {
2888 dw->buffer[row][column] =
2889 dw->buffer[row][column - 1];
2890 }
2891 dw->buffer[row][column] = 0;
2892 }
2893 break;
2894
2895 case DIR_RIGHT_LEFT:
2896 dw->curr_row = 0;
2897 if (column + 1 < dw->row_count) {
2898 dw->curr_column = column + 1;
2899 break;
2900 }
2901 dw->streamed >>= 1;
2902 for (row = 0; row < dw->row_count; ++row) {
2903 for (column = 0;
2904 column < dw->column_count - 1; ++column) {
2905 dw->buffer[row][column] =
2906 dw->buffer[row][column + 1];
2907 }
2908 dw->buffer[row][column] = 0;
2909 }
2910 break;
2911
2912 case DIR_TOP_BOTTOM:
2913 dw->curr_column = 0;
2914 if (row > 0) {
2915 dw->curr_row = row - 1;
2916 break;
2917 }
2918 dw->streamed = (dw->streamed << 1)
2919 & ~(1 << dw->row_count);
2920 memmove (&dw->buffer[1], &dw->buffer[0],
2921 sizeof (dw->buffer[0]) * (dw->row_count - 1));
2922 memset (&dw->buffer[0], 0, sizeof (dw->buffer[0]));
2923 break;
2924
2925 case DIR_BOTTOM_TOP:
2926 dw->curr_column = 0;
2927 if (row + 1 < dw->row_count) {
2928 dw->curr_row = row + 1;
2929 break;
2930 }
2931 dw->streamed >>= 1;
2932 memmove (&dw->buffer[0], &dw->buffer[1],
2933 sizeof (dw->buffer[0]) * (dw->row_count - 1));
2934 memset (&dw->buffer[row], 0, sizeof (dw->buffer[0]));
2935 break;
2936 }
2937
2938 return TRUE;
2939}
2940
2941static vbi_bool
2942dtvcc_form_feed (struct dtvcc_decoder * dc,
2943 struct dtvcc_service * ds)
2944{
2945 struct dtvcc_window *dw;
2946 dtvcc_window_map window_map;
2947
2948 dw = ds->curr_window;
2949 if (NULL == dw) {
2950 ds->error_line = __LINE__;
2951 return FALSE;
2952 }
2953
2954 window_map = 1 << dtvcc_window_id (ds, dw);
2955
2956 if (!dtvcc_clear_windows (dc, ds, window_map))
2957 return FALSE;
2958
2959 dw->curr_row = 0;
2960 dw->curr_column = 0;
2961
2962 return TRUE;
2963}
2964
2965static vbi_bool
2966dtvcc_backspace (struct dtvcc_decoder * dc,
2967 struct dtvcc_service * ds)
2968{
2969 struct dtvcc_window *dw;
2970 unsigned int row;
2971 unsigned int column;
2972 unsigned int mask;
2973
2974 dc = dc; /* unused */
2975
2976 dw = ds->curr_window;
2977 if (NULL == dw) {
2978 ds->error_line = __LINE__;
2979 return FALSE;
2980 }
2981
2982 row = dw->curr_row;
2983 column = dw->curr_column;
2984
2985 switch (dw->style.print_direction) {
2986 case DIR_LEFT_RIGHT:
2987 mask = 1 << row;
2988 if (column-- <= 0)
2989 return TRUE;
2990 break;
2991
2992 case DIR_RIGHT_LEFT:
2993 mask = 1 << row;
2994 if (++column >= dw->column_count)
2995 return TRUE;
2996 break;
2997
2998 case DIR_TOP_BOTTOM:
2999 mask = 1 << column;
3000 if (row-- <= 0)
3001 return TRUE;
3002 break;
3003
3004 case DIR_BOTTOM_TOP:
3005 mask = 1 << column;
3006 if (++row >= dw->row_count)
3007 return TRUE;
3008 break;
3009 }
3010
3011 if (0 != dw->buffer[row][column]) {
3012 dw->streamed &= ~mask;
3013 dw->buffer[row][column] = 0;
3014 }
3015
3016 dw->curr_row = row;
3017 dw->curr_column = column;
3018
3019 return TRUE;
3020}
3021
3022static vbi_bool
3023dtvcc_hor_carriage_return (struct dtvcc_decoder * dc,
3024 struct dtvcc_service * ds)
3025{
3026 struct dtvcc_window *dw;
3027 unsigned int row;
3028 unsigned int column;
3029 unsigned int mask;
3030
3031 dc = dc; /* unused */
3032
3033 dw = ds->curr_window;
3034 if (NULL == dw) {
3035 ds->error_line = __LINE__;
3036 return FALSE;
3037 }
3038
3039 row = dw->curr_row;
3040 column = dw->curr_column;
3041
3042 switch (dw->style.print_direction) {
3043 case DIR_LEFT_RIGHT:
3044 case DIR_RIGHT_LEFT:
3045 mask = 1 << row;
3046 memset (&dw->buffer[row][0], 0,
3047 sizeof (dw->buffer[0]));
3048 if (DIR_LEFT_RIGHT == dw->style.print_direction)
3049 dw->curr_column = 0;
3050 else
3051 dw->curr_column = dw->column_count - 1;
3052 break;
3053
3054 case DIR_TOP_BOTTOM:
3055 case DIR_BOTTOM_TOP:
3056 mask = 1 << column;
3057 for (row = 0; row < dw->column_count; ++row)
3058 dw->buffer[row][column] = 0;
3059 if (DIR_TOP_BOTTOM == dw->style.print_direction)
3060 dw->curr_row = 0;
3061 else
3062 dw->curr_row = dw->row_count - 1;
3063 break;
3064 }
3065
3066 dw->streamed &= ~mask;
3067
3068 return TRUE;
3069}
3070
3071static vbi_bool
3072dtvcc_delete_windows (struct dtvcc_decoder * dc,
3073 struct dtvcc_service * ds,
3074 dtvcc_window_map window_map)
3075{
3076 struct dtvcc_window *dw;
3077
3078 dw = ds->curr_window;
3079 if (NULL != dw) {
3080 unsigned int window_id;
3081
3082 window_id = dtvcc_window_id (ds, dw);
3083 if (0 != (window_map & (1 << window_id))) {
3084 dtvcc_stream_event (dc, ds, dw, dw->curr_row);
3085 ds->curr_window = NULL;
3086 }
3087 }
3088
3089 ds->created &= ~window_map;
3090
3091 return TRUE;
3092}
3093
3094static vbi_bool
3095dtvcc_command (struct dtvcc_decoder * dc,
3096 struct dtvcc_service * ds,
3097 unsigned int * se_length,
3098 uint8_t * buf,
3099 unsigned int n_bytes)
3100{
3101 unsigned int c;
3102 unsigned int window_id;
3103
3104 c = buf[0];
3105 if ((int8_t) c < 0) {
3106 *se_length = dtvcc_c1_length[c - 0x80];
3107 } else {
3108 *se_length = dtvcc_c0_length[c >> 3];
3109 }
3110
3111 if (*se_length > n_bytes) {
3112 ds->error_line = __LINE__;
3113 return FALSE;
3114 }
3115
3116 switch (c) {
3117 case 0x08: /* BS Backspace */
3118 return dtvcc_backspace (dc, ds);
3119
3120 case 0x0C: /* FF Form Feed */
3121 return dtvcc_form_feed (dc, ds);
3122
3123 case 0x0D: /* CR Carriage Return */
3124 return dtvcc_carriage_return (dc, ds);
3125
3126 case 0x0E: /* HCR Horizontal Carriage Return */
3127 return dtvcc_hor_carriage_return (dc, ds);
3128
3129 case 0x80 ... 0x87: /* CWx SetCurrentWindow */
3130 window_id = c & 7;
3131 if (0 == (ds->created & (1 << window_id))) {
3132 ds->error_line = __LINE__;
3133 return FALSE;
3134 }
3135 ds->curr_window = &ds->window[window_id];
3136 return TRUE;
3137
3138 case 0x88: /* CLW ClearWindows */
3139 return dtvcc_clear_windows (dc, ds, buf[1]);
3140
3141 case 0x89: /* DSW DisplayWindows */
3142 return dtvcc_display_windows (dc, ds, c, buf[1]);
3143
3144 case 0x8A: /* HDW HideWindows */
3145 return dtvcc_display_windows (dc, ds, c, buf[1]);
3146
3147 case 0x8B: /* TGW ToggleWindows */
3148 return dtvcc_display_windows (dc, ds, c, buf[1]);
3149
3150 case 0x8C: /* DLW DeleteWindows */
3151 return dtvcc_delete_windows (dc, ds, buf[1]);
3152
3153 case 0x8F: /* RST Reset */
3154 dtvcc_reset_service (ds);
3155 return TRUE;
3156
3157 case 0x90: /* SPA SetPenAttributes */
3158 return dtvcc_set_pen_attributes (ds, buf);
3159
3160 case 0x91: /* SPC SetPenColor */
3161 return dtvcc_set_pen_color (ds, buf);
3162
3163 case 0x92: /* SPL SetPenLocation */
3164 return dtvcc_set_pen_location (dc, ds, buf);
3165
3166 case 0x97: /* SWA SetWindowAttributes */
3167 return dtvcc_set_window_attributes (ds, buf);
3168
3169 case 0x98 ... 0x9F: /* DFx DefineWindow */
3170 return dtvcc_define_window (dc, ds, buf);
3171
3172 default:
3173 return TRUE;
3174 }
3175}
3176
3177static vbi_bool
3178dtvcc_decode_se (struct dtvcc_decoder * dc,
3179 struct dtvcc_service * ds,
3180 unsigned int * se_length,
3181 uint8_t * buf,
3182 unsigned int n_bytes)
3183{
3184 unsigned int c;
3185
3186 c = buf[0];
3187 if (likely (0 != (c & 0x60))) {
3188 /* G0/G1 character. */
3189 *se_length = 1;
3190 return dtvcc_put_char (dc, ds, c);
3191 }
3192
3193 if (0x10 != c) {
3194 /* C0/C1 control code. */
3195 return dtvcc_command (dc, ds, se_length,
3196 buf, n_bytes);
3197 }
3198
3199 if (unlikely (n_bytes < 2)) {
3200 ds->error_line = __LINE__;
3201 return FALSE;
3202 }
3203
3204 c = buf[1];
3205 if (likely (0 != (c & 0x60))) {
3206 /* G2/G3 character. */
3207 *se_length = 2;
3208 return dtvcc_put_char (dc, ds, 0x1000 | c);
3209 }
3210
3211 /* CEA 708-C defines no C2 or C3 commands. */
3212
3213 if ((int8_t) c >= 0) {
3214 /* C2 code. */
3215 *se_length = (c >> 3) + 2;
3216 } else if (c < 0x90) {
3217 /* C3 Fixed Length Commands. */
3218 *se_length = (c >> 3) - 10;
3219 } else {
3220 /* C3 Variable Length Commands. */
3221
3222 if (unlikely (n_bytes < 3)) {
3223 ds->error_line = __LINE__;
3224 return FALSE;
3225 }
3226
3227 /* type [2], zero_bit [1],
3228 length [5] */
3229 *se_length = (buf[2] & 0x1F) + 3;
3230 }
3231
3232 if (unlikely (n_bytes < *se_length)) {
3233 ds->error_line = __LINE__;
3234 return FALSE;
3235 }
3236
3237 return TRUE;
3238}
3239
3240static vbi_bool
3241dtvcc_decode_syntactic_elements (struct dtvcc_decoder * dc,
3242 struct dtvcc_service * ds,
3243 uint8_t * buf,
3244 unsigned int n_bytes)
3245{
3246 ds->timestamp = dc->timestamp;
3247
3248 while (n_bytes > 0) {
3249 unsigned int se_length;
3250
3251 if (0x8D /* DLY */ == *buf
3252 || 0x8E /* DLC */ == *buf) {
3253 /* FIXME ignored for now. */
3254 ++buf;
3255 --n_bytes;
3256 continue;
3257 }
3258
3259 if (!dtvcc_decode_se (dc, ds,
3260 &se_length,
3261 buf, n_bytes)) {
3262 return FALSE;
3263 }
3264
3265 buf += se_length;
3266 n_bytes -= se_length;
3267 }
3268
3269 return TRUE;
3270}
3271
3272void
3273dtvcc_decode_packet (struct dtvcc_decoder * dc,
3274 const struct timeval * tv,
3275 int64_t pts)
3276{
3277 unsigned int packet_size_code;
3278 unsigned int packet_size;
3279 unsigned int i;
3280
3281 dc->timestamp.sys = *tv;
3282 dc->timestamp.pts = pts;
3283
3284 /* Packet Layer. */
3285
3286 /* sequence_number [2], packet_size_code [6],
3287 packet_data [n * 8] */
3288
3289 if (dc->next_sequence_number >= 0
3290 && 0 != ((dc->packet[0] ^ dc->next_sequence_number) & 0xC0)) {
3291 dtvcc_reset (dc);
3292 return;
3293 }
3294
3295 dc->next_sequence_number = dc->packet[0] + 0x40;
3296
3297 packet_size_code = dc->packet[0] & 0x3F;
3298 packet_size = 128;
3299 if (packet_size_code > 0)
3300 packet_size = packet_size_code * 2;
3301
3302 /* CEA 708-C Section 5: Apparently packet_size need not be
3303 equal to the actually transmitted amount of data. */
3304 if (packet_size > dc->packet_size) {
3305 dtvcc_reset (dc);
3306 return;
3307 }
3308
3309 /* Service Layer. */
3310
3311 /* CEA 708-C Section 6.2.5, 6.3: Service Blocks and syntactic
3312 elements must not cross Caption Channel Packet
3313 boundaries. */
3314
3315 for (i = 1; i < packet_size;) {
3316 unsigned int service_number;
3317 unsigned int block_size;
3318 unsigned int header_size;
3319 unsigned int c;
3320
3321 header_size = 1;
3322
3323 /* service_number [3], block_size [5],
3324 (null_fill [2], extended_service_number [6]),
3325 (Block_data [n * 8]) */
3326
3327 c = dc->packet[i];
3328 service_number = (c & 0xE0) >> 5;
3329
3330 /* CEA 708-C Section 6.3: Ignore block_size if
3331 service_number is zero. */
3332 if (0 == service_number) {
3333 /* NULL Service Block Header, no more data in
3334 this Caption Channel Packet. */
3335 break;
3336 }
3337
3338 /* CEA 708-C Section 6.2.1: Apparently block_size zero
3339 is valid, although properly it should only occur in
3340 NULL Service Block Headers. */
3341 block_size = c & 0x1F;
3342
3343 if (7 == service_number) {
3344 if (i + 1 > packet_size)
3345 goto service_block_incomplete;
3346
3347 header_size = 2;
3348 c = dc->packet[i + 1];
3349
3350 /* We also check the null_fill bits. */
3351 if (c < 7 || c > 63)
3352 goto invalid_service_block;
3353
3354 service_number = c;
3355 }
3356
3357 if (i + header_size + block_size > packet_size)
3358 goto service_block_incomplete;
3359
3360 if (service_number <= 2) {
3361 struct dtvcc_service *ds;
3362 unsigned int in;
3363
3364 ds = &dc->service[service_number - 1];
3365 in = ds->service_data_in;
3366 memcpy (ds->service_data + in,
3367 dc->packet + i + header_size,
3368 block_size);
3369 ds->service_data_in = in + block_size;
3370 }
3371
3372 i += header_size + block_size;
3373 }
3374
3375 for (i = 0; i < 2; ++i) {
3376 struct dtvcc_service *ds;
3377 struct program *pr;
3378 vbi_bool success;
3379
3380 ds = &dc->service[i];
3381 if (0 == ds->service_data_in)
3382 continue;
3383
3384 success = dtvcc_decode_syntactic_elements
3385 (dc, ds, ds->service_data, ds->service_data_in);
3386
3387 ds->service_data_in = 0;
3388
3389 if (success)
3390 continue;
3391
3392 dtvcc_reset_service (ds);
3393 }
3394
3395 return;
3396
3397 invalid_service_block:
3398 {
3399 dtvcc_reset (dc);
3400 return;
3401 }
3402
3403 service_block_incomplete:
3404 {
3405 dtvcc_reset (dc);
3406 return;
3407 }
3408
3409}
3410
3411static void
3412dtvcc_reset_service (struct dtvcc_service * ds)
3413{
3414 ds->curr_window = NULL;
3415 ds->created = 0;
3416
3417 cc_timestamp_reset (&ds->timestamp);
3418}
3419
3420void
3421dtvcc_reset (struct dtvcc_decoder * dc)
3422{
3423 dtvcc_reset_service (&dc->service[0]);
3424 dtvcc_reset_service (&dc->service[1]);
3425
3426 dc->packet_size = 0;
3427 dc->next_sequence_number = -1;
3428}
3429
3430void
3431dtvcc_init (struct dtvcc_decoder * dc)
3432{
3433 dtvcc_reset (dc);
3434
3435 cc_timestamp_reset (&dc->timestamp);
3436}
3437
3438static void dtvcc_window_to_page(vbi_decoder *vbi, struct dtvcc_window *dw, struct vbi_page *pg)
3439{
3440 int i, j, c;
3441 vbi_char ac;
3442 vbi_opacity fg_opacity, bg_opacity;
3443
3444 static const vbi_opacity vbi_opacity_map[]={
3445 VBI_OPAQUE,
3446 VBI_OPAQUE,
3447 VBI_SEMI_TRANSPARENT,
3448 VBI_TRANSPARENT_SPACE
3449 };
3450
3451 memset(pg, 0, sizeof(struct vbi_page));
3452#if 1
3453 pg->rows = dw->row_count;
3454 pg->columns = dw->column_count;
3455
3456 dtvcc_set_page_color_map(vbi, pg);
3457
3458 for (i=0; i<pg->rows; i++)
3459 {
3460 for (j=0; j<pg->columns; j++)
3461 {
3462 memset(&ac, 0, sizeof(ac));
3463 /*use the curr_pen to draw all the text, actually this isn't reasonable*/
3464 if (dw->curr_pen.style.fg_opacity >= N_ELEMENTS(vbi_opacity_map)){
3465 fg_opacity = VBI_OPAQUE;
3466 }else{
3467 fg_opacity = vbi_opacity_map[dw->curr_pen.style.fg_opacity];
3468 }
3469
3470 if (dw->curr_pen.style.bg_opacity >= N_ELEMENTS(vbi_opacity_map)){
3471 bg_opacity = VBI_OPAQUE;
3472 }else{
3473 bg_opacity = vbi_opacity_map[dw->curr_pen.style.bg_opacity];
3474 }
3475
3476 ac.opacity = (fg_opacity<<4) | bg_opacity;
3477 ac.foreground = dtvcc_map_color(dw->curr_pen.style.fg_color);
3478 ac.background = dtvcc_map_color(dw->curr_pen.style.bg_color);
3479
3480 c = dw->buffer[i][j];
3481 if (0 == c) {
3482 ac.unicode = 0x20;
3483 ac.opacity = VBI_TRANSPARENT_SPACE;
3484 } else {
3485 ac.unicode = dtvcc_unicode (c);
3486 if (0 == ac.unicode) {
3487 ac.unicode = 0x20;
3488 }
3489 }
3490
3491 pg->text[i*pg->columns + j] = ac;
3492 }
3493 }
3494#else
3495 pg->rows = 15;
3496 pg->columns = 42;
3497 dtvcc_set_page_color_map(vbi, pg);
3498 for (i=0; i<pg->rows; i++)
3499 {
3500 for (j=0; j<pg->columns; j++)
3501 {
3502 memset(&ac, 0, sizeof(ac));
3503 ac.opacity = VBI_OPAQUE;
3504 ac.foreground = VBI_WHITE;
3505 ac.background = VBI_BLACK;
3506 if (j == 0)
3507 c = '0' + (i%10);
3508 else
3509 c = '0' + (j%10);
3510 ac.unicode = dtvcc_unicode (c);
3511 LOGI("TEXT(%x): %c", ac.unicode, ac.unicode);
3512 pg->text[i*pg->columns + j] = ac;
3513 }
3514 }
3515#endif
3516 pg->screen_color = 0;
3517 pg->screen_opacity = VBI_OPAQUE;
3518 //pg->font[0] = vbi_font_descriptors; /* English */
3519 //pg->font[1] = vbi_font_descriptors;
3520}
3521
3522static int dtvcc_compare_window_priority(const void *window1, const void *window2)
3523{
3524 struct dtvcc_window *w1 = (struct dtvcc_window*)window1;
3525 struct dtvcc_window *w2 = (struct dtvcc_window*)window2;
3526
3527 return (w1->priority - w2->priority);
3528}
3529
3530static void dtvcc_get_visible_windows(struct dtvcc_service *ds, int *cnt, struct dtvcc_window **windows)
3531{
3532 int i, j = 0;
3533
3534 for (i=0; i<N_ELEMENTS(ds->window); i++){
3535 if (ds->window[i].visible && j < *cnt){
3536 windows[j++] = &ds->window[i];
3537 }
3538 }
3539
3540 qsort(windows, j, sizeof(windows[0]), dtvcc_compare_window_priority);
3541
3542 *cnt = j;
3543}
3544
3545void tvcc_fetch_page(struct tvcc_decoder *td, int pgno, int *sub_cnt, struct vbi_page *sub_pages)
3546{
3547 int sub_pg = 0;
3548
3549 if (pgno < 1 || pgno > 14 || *sub_cnt <= 0)
3550 goto fetch_done;
3551
3552 if (pgno <= 8){
3553 if (vbi_fetch_cc_page(td->vbi, &sub_pages[0], pgno, 1)){
3554 sub_pg = 1;
3555 sub_pages[0].pgno = pgno;
3556 }
3557 }else{
3558 int i;
3559 struct dtvcc_service *ds = &td->dtvcc.service[pgno - 1 - 8];
3560 struct dtvcc_window *dw;
3561 struct dtvcc_window *visible_windows[8];
3562
3563 sub_pg = *sub_cnt;
3564 if (sub_pg > 8)
3565 sub_pg = 8;
3566
3567 dtvcc_get_visible_windows(ds, &sub_pg, visible_windows);
3568
3569 for (i=0; i<sub_pg; i++){
3570 dw = visible_windows[i];
3571
3572 dtvcc_window_to_page(td->vbi, dw, &sub_pages[i]);
3573
3574 sub_pages[i].vbi = td->vbi;
3575 sub_pages[i].pgno = pgno;
3576 sub_pages[i].subno = dw - ds->window;
3577 }
3578 }
3579
3580fetch_done:
3581 *sub_cnt = sub_pg;
3582}
3583
3584/* ATSC A/53 Part 4:2007 Closed Caption Data decoder */
3585
3586/* Note pts may be < 0 if no PTS was received. */
3587void
3588tvcc_decode_data (struct tvcc_decoder *td,
3589 int64_t pts,
3590 const uint8_t * buf,
3591 unsigned int n_bytes)
3592{
3593 unsigned int process_cc_data_flag;
3594 unsigned int cc_count;
3595 unsigned int i;
3596 vbi_bool dtvcc;
3597 struct timeval now;
3598
3599 if (buf[0] != 0x03)
3600 return;
3601 process_cc_data_flag = buf[1] & 0x40;
3602 if (!process_cc_data_flag)
3603 return;
3604
3605 cc_count = buf[1] & 0x1F;
3606 dtvcc = FALSE;
3607
3608 pthread_mutex_lock(&td->mutex);
3609 for (i = 0; i < cc_count; ++i) {
3610 unsigned int b0;
3611 unsigned int cc_valid;
3612 enum cc_type cc_type;
3613 unsigned int cc_data_1;
3614 unsigned int cc_data_2;
3615 unsigned int j;
3616
3617 b0 = buf[3 + i * 3];
3618 cc_valid = b0 & 4;
3619 cc_type = (enum cc_type)(b0 & 3);
3620 cc_data_1 = buf[4 + i * 3];
3621 cc_data_2 = buf[5 + i * 3];
3622
3623 switch (cc_type) {
3624 case NTSC_F1:
3625 case NTSC_F2:
3626 /* Note CEA 708-C Table 4: Only one NTSC pair
3627 will be present in field picture user_data
3628 or in progressive video pictures, and up to
3629 three can occur if the frame rate < 30 Hz
3630 or repeat_first_field = 1. */
3631 if (!cc_valid || i >= 3 || dtvcc) {
3632 /* Illegal, invalid or filler. */
3633 break;
3634 }
3635#if 0
3636 cc_feed (&td->cc, &buf[12 + i * 3],
3637 /* line */ (NTSC_F1 == cc_type) ? 21 : 284,
3638 &now, pts);
3639#endif
3640 vbi_decode_caption(td->vbi, (NTSC_F1 == cc_type) ? 21 : 284, &buf[4 + i * 3]);
3641 break;
3642
3643 case DTVCC_DATA:
3644 j = td->dtvcc.packet_size;
3645 if (j <= 0) {
3646 /* Missed packet start. */
3647 break;
3648 } else if (!cc_valid) {
3649 /* End of DTVCC packet. */
3650 dtvcc_decode_packet (&td->dtvcc,
3651 &now, pts);
3652 td->dtvcc.packet_size = 0;
3653 } else if (j >= 128) {
3654 /* Packet buffer overflow. */
3655 dtvcc_reset (&td->dtvcc);
3656 td->dtvcc.packet_size = 0;
3657 } else {
3658 td->dtvcc.packet[j] = cc_data_1;
3659 td->dtvcc.packet[j + 1] = cc_data_2;
3660 td->dtvcc.packet_size = j + 2;
3661 }
3662 break;
3663
3664 case DTVCC_START:
3665 dtvcc = TRUE;
3666 j = td->dtvcc.packet_size;
3667 if (j > 0) {
3668 /* End of DTVCC packet. */
3669 dtvcc_decode_packet (&td->dtvcc,
3670 &now, pts);
3671 }
3672 if (!cc_valid) {
3673 /* No new data. */
3674 td->dtvcc.packet_size = 0;
3675 } else {
3676 td->dtvcc.packet[0] = cc_data_1;
3677 td->dtvcc.packet[1] = cc_data_2;
3678 td->dtvcc.packet_size = 2;
3679 }
3680 break;
3681 }
3682 }
3683 pthread_mutex_unlock(&td->mutex);
3684}
3685
3686void tvcc_init(struct tvcc_decoder *td)
3687{
3688 pthread_mutex_init(&td->mutex, NULL);
3689 dtvcc_init(&td->dtvcc);
3690 cc_init(&td->cc);
3691 td->vbi = vbi_decoder_new();
3692}
3693
3694void tvcc_destroy(struct tvcc_decoder *td)
3695{
3696 if (td){
3697 vbi_decoder_delete(td->vbi);
3698 td->vbi = NULL;
3699 pthread_mutex_destroy(&td->mutex);
3700 }
3701}
3702
3703void tvcc_reset(struct tvcc_decoder *td)
3704{
3705 dtvcc_reset(&td->dtvcc);
3706 cc_reset(&td->cc);
3707}
3708
3709