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