2 * From ccextractor, leave this file as intact and close to the original as possible so that
3 * it is easy to patch in fixes - even though this file contains code that we don't need.
5 * Note that the SRT sub generation from CC could be useful for mkv subs.
8 #include "deccc608sub.h"
11 * ccextractor static configuration variables.
13 static int debug_608 = 0;
14 static int trim_subs = 0;
15 static int nofontcolor = 0;
16 static enum encoding_type encoding = ENC_UTF_8;
17 static int cc_channel = 1;
18 static enum output_format write_format = OF_SRT;
19 static int sentence_cap = 1;
20 static int subs_delay = 0;
21 static LLONG screens_to_process = -1;
22 static int processed_enough = 0;
23 static int gui_mode_reports = 0;
24 static int norollup = 1;
25 static int direct_rollup = 0;
27 static LLONG get_fts(struct s_write *wb)
32 #define fatal(N, ...) // N
33 #define XMLRPC_APPEND(N, ...) // N
35 int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10};
36 // Relationship between the first PAC byte and the row number
38 // The following enc_buffer is not used at the moment, if it does get used
39 // we need to bring it into the swrite struct. Same for "str".
40 #define INITIAL_ENC_BUFFER_CAPACITY 2048
42 unsigned char *enc_buffer=NULL; // Generic general purpose buffer
43 unsigned char str[2048]; // Another generic general purpose buffer
44 unsigned enc_buffer_used;
45 unsigned enc_buffer_capacity;
47 #define GUARANTEE(length) if (length>enc_buffer_capacity) \
48 {enc_buffer_capacity*=2; enc_buffer=(unsigned char*) realloc (enc_buffer, enc_buffer_capacity); \
49 if (enc_buffer==NULL) { fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory, bailing out\n"); } \
52 const unsigned char pac2_attribs[][3]= // Color, font, ident
54 {COL_WHITE, FONT_REGULAR, 0}, // 0x40 || 0x60
55 {COL_WHITE, FONT_UNDERLINED, 0}, // 0x41 || 0x61
56 {COL_GREEN, FONT_REGULAR, 0}, // 0x42 || 0x62
57 {COL_GREEN, FONT_UNDERLINED, 0}, // 0x43 || 0x63
58 {COL_BLUE, FONT_REGULAR, 0}, // 0x44 || 0x64
59 {COL_BLUE, FONT_UNDERLINED, 0}, // 0x45 || 0x65
60 {COL_CYAN, FONT_REGULAR, 0}, // 0x46 || 0x66
61 {COL_CYAN, FONT_UNDERLINED, 0}, // 0x47 || 0x67
62 {COL_RED, FONT_REGULAR, 0}, // 0x48 || 0x68
63 {COL_RED, FONT_UNDERLINED, 0}, // 0x49 || 0x69
64 {COL_YELLOW, FONT_REGULAR, 0}, // 0x4a || 0x6a
65 {COL_YELLOW, FONT_UNDERLINED, 0}, // 0x4b || 0x6b
66 {COL_MAGENTA, FONT_REGULAR, 0}, // 0x4c || 0x6c
67 {COL_MAGENTA, FONT_UNDERLINED, 0}, // 0x4d || 0x6d
68 {COL_WHITE, FONT_ITALICS, 0}, // 0x4e || 0x6e
69 {COL_WHITE, FONT_UNDERLINED_ITALICS, 0}, // 0x4f || 0x6f
70 {COL_WHITE, FONT_REGULAR, 0}, // 0x50 || 0x70
71 {COL_WHITE, FONT_UNDERLINED, 0}, // 0x51 || 0x71
72 {COL_WHITE, FONT_REGULAR, 4}, // 0x52 || 0x72
73 {COL_WHITE, FONT_UNDERLINED, 4}, // 0x53 || 0x73
74 {COL_WHITE, FONT_REGULAR, 8}, // 0x54 || 0x74
75 {COL_WHITE, FONT_UNDERLINED, 8}, // 0x55 || 0x75
76 {COL_WHITE, FONT_REGULAR, 12}, // 0x56 || 0x76
77 {COL_WHITE, FONT_UNDERLINED, 12}, // 0x57 || 0x77
78 {COL_WHITE, FONT_REGULAR, 16}, // 0x58 || 0x78
79 {COL_WHITE, FONT_UNDERLINED, 16}, // 0x59 || 0x79
80 {COL_WHITE, FONT_REGULAR, 20}, // 0x5a || 0x7a
81 {COL_WHITE, FONT_UNDERLINED, 20}, // 0x5b || 0x7b
82 {COL_WHITE, FONT_REGULAR, 24}, // 0x5c || 0x7c
83 {COL_WHITE, FONT_UNDERLINED, 24}, // 0x5d || 0x7d
84 {COL_WHITE, FONT_REGULAR, 28}, // 0x5e || 0x7e
85 {COL_WHITE, FONT_UNDERLINED, 28} // 0x5f || 0x7f
89 unsigned char encoded_crlf[16];
90 unsigned int encoded_crlf_length;
91 unsigned char encoded_br[16];
92 unsigned int encoded_br_length;
95 unsigned char usercolor_rgb[8]="";
96 enum color_code default_color=COL_WHITE;
98 const char *sami_header= // TODO: Revise the <!-- comments
101 <STYLE TYPE=\"text/css\">\n\
103 P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n\
104 text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n\
105 .UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n\
111 const char *command_type[] =
114 "EDM - EraseDisplayedMemory",
115 "RCL - ResumeCaptionLoading",
116 "EOC - End Of Caption",
117 "TO1 - Tab Offset, 1 column",
118 "TO2 - Tab Offset, 2 column",
119 "TO3 - Tab Offset, 3 column",
120 "RU2 - Roll up 2 rows",
121 "RU3 - Roll up 3 rows",
122 "RU4 - Roll up 4 rows",
123 "CR - Carriage Return",
124 "ENM - Erase non-displayed memory",
126 "RTD - Resume Text Display"
129 const char *font_text[]=
137 const char *cc_modes_text[]=
142 const char *color_text[][2]=
145 {"green","<font color=\"#00ff00\">"},
146 {"blue","<font color=\"#0000ff\">"},
147 {"cyan","<font color=\"#00ffff\">"},
148 {"red","<font color=\"#ff0000\">"},
149 {"yellow","<font color=\"#ffff00\">"},
150 {"magenta","<font color=\"#ff00ff\">"},
151 {"userdefined","<font color=\""}
154 int general_608_init (struct s_write *wb)
157 * Not currently used.
161 enc_buffer=(unsigned char *) malloc (INITIAL_ENC_BUFFER_CAPACITY);
162 if (enc_buffer==NULL)
164 enc_buffer_capacity=INITIAL_ENC_BUFFER_CAPACITY;
169 wb->subline = malloc(2048);
177 wb->new_sentence = 1;
181 wb->hb_buffer = NULL;
182 wb->hb_last_buffer = NULL;
188 * Free up CC memory - don't call this from HB just yet since it will cause
189 * parallel encodes to fail - to be honest they will be stuffed anyway since
190 * the CC's may be overwriting the buffers.
192 void general_608_close (struct s_write *wb)
196 enc_buffer_capacity = 0;
203 if( wb->hb_buffer ) {
204 hb_buffer_close( &wb->hb_buffer );
211 void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
213 unsigned char c1='?';
216 // Regular line-21 character set, mostly ASCII except these exceptions
219 case 0x2a: // lowercase a, acute accent
222 case 0x5c: // lowercase e, acute accent
225 case 0x5e: // lowercase i, acute accent
228 case 0x5f: // lowercase o, acute accent
231 case 0x60: // lowercase u, acute accent
234 case 0x7b: // lowercase c with cedilla
237 case 0x7c: // division symbol
240 case 0x7d: // uppercase N tilde
243 case 0x7e: // lowercase n tilde
255 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
256 // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
257 case 0x80: // Registered symbol (R)
260 case 0x81: // degree sign
263 case 0x82: // 1/2 symbol
266 case 0x83: // Inverted (open) question mark
269 case 0x84: // Trademark symbol (TM) - Does not exist in Latin 1
271 case 0x85: // Cents symbol
274 case 0x86: // Pounds sterling
277 case 0x87: // Music note - Not in latin 1, so we use 'pilcrow'
280 case 0x88: // lowercase a, grave accent
283 case 0x89: // transparent space, we make it regular
286 case 0x8a: // lowercase e, grave accent
289 case 0x8b: // lowercase a, circumflex accent
292 case 0x8c: // lowercase e, circumflex accent
295 case 0x8d: // lowercase i, circumflex accent
298 case 0x8e: // lowercase o, circumflex accent
301 case 0x8f: // lowercase u, circumflex accent
304 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
305 // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
306 case 0x90: // capital letter A with acute
309 case 0x91: // capital letter E with acute
312 case 0x92: // capital letter O with acute
315 case 0x93: // capital letter U with acute
318 case 0x94: // capital letter U with diaresis
321 case 0x95: // lowercase letter U with diaeresis
324 case 0x96: // apostrophe
327 case 0x97: // inverted exclamation mark
330 case 0x98: // asterisk
333 case 0x99: // apostrophe (yes, duped). See CCADI source code.
336 case 0x9a: // hyphen-minus
339 case 0x9b: // copyright sign
342 case 0x9c: // Service Mark - not available in latin 1
344 case 0x9d: // Full stop (.)
347 case 0x9e: // Quoatation mark
350 case 0x9f: // Quoatation mark
353 case 0xa0: // uppercase A, grave accent
356 case 0xa1: // uppercase A, circumflex
359 case 0xa2: // uppercase C with cedilla
362 case 0xa3: // uppercase E, grave accent
365 case 0xa4: // uppercase E, circumflex
368 case 0xa5: // capital letter E with diaresis
371 case 0xa6: // lowercase letter e with diaresis
374 case 0xa7: // uppercase I, circumflex
377 case 0xa8: // uppercase I, with diaresis
380 case 0xa9: // lowercase i, with diaresis
383 case 0xaa: // uppercase O, circumflex
386 case 0xab: // uppercase U, grave accent
389 case 0xac: // lowercase u, grave accent
392 case 0xad: // uppercase U, circumflex
395 case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
398 case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
401 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
402 // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
403 case 0xb0: // Uppercase A, tilde
406 case 0xb1: // Lowercase a, tilde
409 case 0xb2: // Uppercase I, acute accent
412 case 0xb3: // Uppercase I, grave accent
415 case 0xb4: // Lowercase i, grave accent
418 case 0xb5: // Uppercase O, grave accent
421 case 0xb6: // Lowercase o, grave accent
424 case 0xb7: // Uppercase O, tilde
427 case 0xb8: // Lowercase o, tilde
430 case 0xb9: // Open curly brace
433 case 0xba: // Closing curly brace
436 case 0xbb: // Backslash
442 case 0xbd: // Underscore
445 case 0xbe: // Pipe (broken bar)
451 case 0xc0: // Uppercase A, umlaut
454 case 0xc1: // Lowercase A, umlaut
457 case 0xc2: // Uppercase O, umlaut
460 case 0xc3: // Lowercase o, umlaut
463 case 0xc4: // Esszett (sharp S)
466 case 0xc5: // Yen symbol
469 case 0xc6: // Currency symbol
472 case 0xc7: // Vertical bar
475 case 0xc8: // Uppercase A, ring
478 case 0xc9: // Lowercase A, ring
481 case 0xca: // Uppercase O, slash
484 case 0xcb: // Lowercase o, slash
487 case 0xcc: // Upper left corner
488 case 0xcd: // Upper right corner
489 case 0xce: // Lower left corner
490 case 0xcf: // Lower right corner
491 default: // For those that don't have representation
492 *buffer='?'; // I'll do it eventually, I promise
493 break; // This are weird chars anyway
498 void get_char_in_unicode (unsigned char *buffer, unsigned char c)
503 case 0x84: // Trademark symbol (TM)
507 case 0x87: // Music note
511 case 0x9c: // Service Mark
515 case 0xcc: // Upper left corner
519 case 0xcd: // Upper right corner
523 case 0xce: // Lower left corner
527 case 0xcf: // Lower right corner
531 default: // Everything else, same as latin-1 followed by 00
532 get_char_in_latin_1 (&c1,c);
540 int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number of bytes used
542 if (c<0x80) // Regular line-21 character set, mostly ASCII except these exceptions
546 case 0x2a: // lowercase a, acute accent
550 case 0x5c: // lowercase e, acute accent
554 case 0x5e: // lowercase i, acute accent
558 case 0x5f: // lowercase o, acute accent
562 case 0x60: // lowercase u, acute accent
566 case 0x7b: // lowercase c with cedilla
570 case 0x7c: // division symbol
574 case 0x7d: // uppercase N tilde
578 case 0x7e: // lowercase n tilde
589 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
590 // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
591 case 0x80: // Registered symbol (R)
595 case 0x81: // degree sign
599 case 0x82: // 1/2 symbol
603 case 0x83: // Inverted (open) question mark
607 case 0x84: // Trademark symbol (TM)
612 case 0x85: // Cents symbol
616 case 0x86: // Pounds sterling
620 case 0x87: // Music note
625 case 0x88: // lowercase a, grave accent
629 case 0x89: // transparent space, we make it regular
632 case 0x8a: // lowercase e, grave accent
636 case 0x8b: // lowercase a, circumflex accent
640 case 0x8c: // lowercase e, circumflex accent
644 case 0x8d: // lowercase i, circumflex accent
648 case 0x8e: // lowercase o, circumflex accent
652 case 0x8f: // lowercase u, circumflex accent
656 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
657 // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
658 case 0x90: // capital letter A with acute
662 case 0x91: // capital letter E with acute
666 case 0x92: // capital letter O with acute
670 case 0x93: // capital letter U with acute
674 case 0x94: // capital letter U with diaresis
678 case 0x95: // lowercase letter U with diaeresis
682 case 0x96: // apostrophe
685 case 0x97: // inverted exclamation mark
689 case 0x98: // asterisk
692 case 0x99: // apostrophe (yes, duped). See CCADI source code.
695 case 0x9a: // hyphen-minus
698 case 0x9b: // copyright sign
702 case 0x9c: // Service mark
707 case 0x9d: // Full stop (.)
710 case 0x9e: // Quoatation mark
713 case 0x9f: // Quoatation mark
716 case 0xa0: // uppercase A, grave accent
720 case 0xa1: // uppercase A, circumflex
724 case 0xa2: // uppercase C with cedilla
728 case 0xa3: // uppercase E, grave accent
732 case 0xa4: // uppercase E, circumflex
736 case 0xa5: // capital letter E with diaresis
740 case 0xa6: // lowercase letter e with diaresis
744 case 0xa7: // uppercase I, circumflex
748 case 0xa8: // uppercase I, with diaresis
752 case 0xa9: // lowercase i, with diaresis
756 case 0xaa: // uppercase O, circumflex
760 case 0xab: // uppercase U, grave accent
764 case 0xac: // lowercase u, grave accent
768 case 0xad: // uppercase U, circumflex
772 case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
776 case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
780 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
781 // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
782 case 0xb0: // Uppercase A, tilde
786 case 0xb1: // Lowercase a, tilde
790 case 0xb2: // Uppercase I, acute accent
794 case 0xb3: // Uppercase I, grave accent
798 case 0xb4: // Lowercase i, grave accent
802 case 0xb5: // Uppercase O, grave accent
806 case 0xb6: // Lowercase o, grave accent
810 case 0xb7: // Uppercase O, tilde
814 case 0xb8: // Lowercase o, tilde
818 case 0xb9: // Open curly brace
821 case 0xba: // Closing curly brace
824 case 0xbb: // Backslash
830 case 0xbd: // Underscore
833 case 0xbe: // Pipe (broken bar)
838 *buffer=0x7e; // Not sure
840 case 0xc0: // Uppercase A, umlaut
844 case 0xc1: // Lowercase A, umlaut
848 case 0xc2: // Uppercase O, umlaut
852 case 0xc3: // Lowercase o, umlaut
856 case 0xc4: // Esszett (sharp S)
860 case 0xc5: // Yen symbol
864 case 0xc6: // Currency symbol
868 case 0xc7: // Vertical bar
871 case 0xc8: // Uppercase A, ring
875 case 0xc9: // Lowercase A, ring
879 case 0xca: // Uppercase O, slash
883 case 0xcb: // Lowercase o, slash
887 case 0xcc: // Upper left corner
892 case 0xcd: // Upper right corner
897 case 0xce: // Lower left corner
902 case 0xcf: // Lower right corner
908 *buffer='?'; // I'll do it eventually, I promise
909 return 1; // This are weird chars anyway
913 unsigned char cctolower (unsigned char c)
915 if (c>='A' && c<='Z')
919 case 0x7d: // uppercase N tilde
921 case 0x90: // capital letter A with acute
923 case 0x91: // capital letter E with acute
925 case 0x92: // capital letter O with acute
927 case 0x93: // capital letter U with acute
929 case 0xa2: // uppercase C with cedilla
931 case 0xa0: // uppercase A, grave accent
933 case 0xa3: // uppercase E, grave accent
935 case 0xa1: // uppercase A, circumflex
937 case 0xa4: // uppercase E, circumflex
939 case 0xa7: // uppercase I, circumflex
941 case 0xaa: // uppercase O, circumflex
943 case 0xad: // uppercase U, circumflex
945 case 0x94: // capital letter U with diaresis
947 case 0xa5: // capital letter E with diaresis
949 case 0xa8: // uppercase I, with diaresis
951 case 0xab: // uppercase U, grave accent
953 case 0xb0: // Uppercase A, tilde
955 case 0xb2: // Uppercase I, acute accent
957 case 0xb3: // Uppercase I, grave accent
959 case 0xb5: // Uppercase O, grave accent
961 case 0xb7: // Uppercase O, tilde
963 case 0xc0: // Uppercase A, umlaut
965 case 0xc2: // Uppercase O, umlaut
967 case 0xc8: // Uppercase A, ring
969 case 0xca: // Uppercase O, slash
975 unsigned char cctoupper (unsigned char c)
977 if (c>='a' && c<='z')
981 case 0x7e: // lowercase n tilde
983 case 0x2a: // lowercase a, acute accent
985 case 0x5c: // lowercase e, acute accent
987 case 0x5e: // lowercase i, acute accent
989 case 0x5f: // lowercase o, acute accent
991 case 0x60: // lowercase u, acute accent
993 case 0x7b: // lowercase c with cedilla
995 case 0x88: // lowercase a, grave accent
997 case 0x8a: // lowercase e, grave accent
999 case 0x8b: // lowercase a, circumflex accent
1001 case 0x8c: // lowercase e, circumflex accent
1003 case 0x8d: // lowercase i, circumflex accent
1005 case 0x8e: // lowercase o, circumflex accent
1007 case 0x8f: // lowercase u, circumflex accent
1009 case 0x95: // lowercase letter U with diaeresis
1011 case 0xa6: // lowercase letter e with diaresis
1013 case 0xa9: // lowercase i, with diaresis
1015 case 0xac: // lowercase u, grave accent
1017 case 0xb1: // Lowercase a, tilde
1019 case 0xb4: // Lowercase i, grave accent
1021 case 0xb6: // Lowercase o, grave accent
1023 case 0xb8: // Lowercase o, tilde
1025 case 0xc1: // Lowercase A, umlaut
1027 case 0xc3: // Lowercase o, umlaut
1029 case 0xc9: // Lowercase A, ring
1031 case 0xcb: // Lowercase o, slash
1038 // Encodes a generic string. Note that since we use the encoders for closed caption
1039 // data, text would have to be encoded as CCs... so using special characters here
1041 unsigned encode_line (unsigned char *buffer, unsigned char *text)
1066 #define ISSEPARATOR(c) (c==' ' || c==0x89 || ispunct(c) \
1067 || c==0x99) // This is the apostrofe. We get it here in CC encoding, not ASCII
1070 void correct_case (int line_num, struct eia608_screen *data)
1073 /* while (i<spell_words) */
1075 /* char *c=(char *) data->characters[line_num]; */
1076 /* size_t len=strlen (spell_correct[i]); */
1077 /* while ((c=strstr (c,spell_lower[i]))!=NULL) */
1079 /* // Make sure it's a whole word (start of line or */
1080 /* // preceded by space, and end of line or followed by */
1082 /* unsigned char prev; */
1083 /* if (c==(char *) data->characters[line_num]) // Beginning of line... */
1084 /* prev=' '; // ...Pretend we had a blank before */
1087 /* unsigned char next; */
1088 /* if (c-(char *) data->characters[line_num]+len==CC608_SCREEN_WIDTH) // End of line... */
1089 /* next=' '; // ... pretend we have a blank later */
1091 /* next=*(c+len); */
1092 /* if ( ISSEPARATOR(prev) && ISSEPARATOR(next)) */
1094 /* memcpy (c,spell_correct[i],len); */
1102 void capitalize (int line_num, struct eia608_screen *data, int *new_sentence)
1106 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1108 switch (data->characters[line_num][i])
1111 case 0x89: // This is a transparent space
1114 case '.': // Fallthrough
1115 case '?': // Fallthrough
1122 data->characters[line_num][i]=cctoupper (data->characters[line_num][i]);
1124 data->characters[line_num][i]=cctolower (data->characters[line_num][i]);
1131 void find_limit_characters (unsigned char *line, int *first_non_blank, int *last_non_blank)
1136 *first_non_blank=-1;
1137 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1139 unsigned char c=line[i];
1140 if (c!=' ' && c!=0x89)
1142 if (*first_non_blank==-1)
1149 unsigned get_decoder_line_basic (unsigned char *buffer, int line_num, struct eia608_screen *data)
1151 unsigned char *line = data->characters[line_num];
1152 int last_non_blank=-1;
1153 int first_non_blank=-1;
1154 unsigned char *orig=buffer; // Keep for debugging
1156 find_limit_characters (line, &first_non_blank, &last_non_blank);
1158 if (first_non_blank==-1)
1165 for (i=first_non_blank;i<=last_non_blank;i++)
1171 bytes=get_char_in_utf_8 (buffer,c);
1174 get_char_in_latin_1 (buffer,c);
1178 get_char_in_unicode (buffer,c);
1185 return (unsigned) (buffer-orig); // Return length
1188 unsigned get_decoder_line_encoded_for_gui (unsigned char *buffer, int line_num, struct eia608_screen *data)
1190 unsigned char *line = data->characters[line_num];
1191 unsigned char *orig=buffer; // Keep for debugging
1192 int first=0, last=31;
1195 find_limit_characters(line,&first,&last);
1196 for (i=first;i<=last;i++)
1198 get_char_in_latin_1 (buffer,line[i]);
1202 return (unsigned) (buffer-orig); // Return length
1206 unsigned get_decoder_line_encoded (unsigned char *buffer, int line_num, struct eia608_screen *data)
1208 int col = COL_WHITE;
1213 unsigned char *line = data->characters[line_num];
1214 unsigned char *orig=buffer; // Keep for debugging
1215 int first=0, last=31;
1217 find_limit_characters(line,&first,&last);
1218 for (i=first;i<=last;i++)
1221 int its_col = data->colors[line_num][i];
1222 if (its_col != col && !nofontcolor)
1224 if (col!=COL_WHITE) // We need to close the previous font tag
1226 buffer+= encode_line (buffer,(unsigned char *) "</font>");
1229 buffer+=encode_line (buffer, (unsigned char*) color_text[its_col][1]);
1230 if (its_col==COL_USERDEFINED)
1232 // The previous sentence doesn't copy the whole
1233 // <font> tag, just up to the quote before the color
1234 buffer+=encode_line (buffer, (unsigned char*) usercolor_rgb);
1235 buffer+=encode_line (buffer, (unsigned char*) "\">");
1240 // Handle underlined
1241 int is_underlined = data->fonts[line_num][i] & FONT_UNDERLINED;
1242 if (is_underlined && underlined==0) // Open underline
1244 buffer+=encode_line (buffer, (unsigned char *) "<u>");
1246 if (is_underlined==0 && underlined) // Close underline
1248 buffer+=encode_line (buffer, (unsigned char *) "</u>");
1250 underlined=is_underlined;
1252 int has_ita = data->fonts[line_num][i] & FONT_ITALICS;
1253 if (has_ita && italics==0) // Open italics
1255 buffer+=encode_line (buffer, (unsigned char *) "<i>");
1257 if (has_ita==0 && italics) // Close italics
1259 buffer+=encode_line (buffer, (unsigned char *) "</i>");
1266 bytes=get_char_in_utf_8 (buffer,line[i]);
1269 get_char_in_latin_1 (buffer,line[i]);
1273 get_char_in_unicode (buffer,line[i]);
1281 buffer+=encode_line (buffer, (unsigned char *) "</i>");
1285 buffer+=encode_line (buffer, (unsigned char *) "</u>");
1287 if (col != COL_WHITE && !nofontcolor)
1289 buffer+=encode_line (buffer, (unsigned char *) "</font>");
1292 return (unsigned) (buffer-orig); // Return length
1296 void delete_all_lines_but_current (struct eia608_screen *data, int row)
1303 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
1304 data->characters[i][CC608_SCREEN_WIDTH]=0;
1305 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
1306 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
1307 data->row_used[i]=0;
1312 void clear_eia608_cc_buffer (struct eia608_screen *data)
1318 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
1319 data->characters[i][CC608_SCREEN_WIDTH]=0;
1320 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
1321 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
1322 data->row_used[i]=0;
1327 void init_eia608 (struct eia608 *data)
1329 data->cursor_column=0;
1331 clear_eia608_cc_buffer (&data->buffer1);
1332 clear_eia608_cc_buffer (&data->buffer2);
1333 data->visible_buffer=1;
1336 data->mode=MODE_POPUP;
1337 // data->current_visible_start_cc=0;
1338 data->current_visible_start_ms=0;
1339 data->srt_counter=0;
1340 data->screenfuls_counter=0;
1342 data->color=default_color;
1343 data->font=FONT_REGULAR;
1344 data->rollup_base_row=14;
1347 struct eia608_screen *get_writing_buffer (struct s_write *wb)
1349 struct eia608_screen *use_buffer=NULL;
1350 switch (wb->data608->mode)
1352 case MODE_POPUP: // Write on the non-visible buffer
1353 if (wb->data608->visible_buffer==1)
1354 use_buffer = &wb->data608->buffer2;
1356 use_buffer = &wb->data608->buffer1;
1358 case MODE_ROLLUP_2: // Write directly to screen
1361 if (wb->data608->visible_buffer==1)
1362 use_buffer = &wb->data608->buffer1;
1364 use_buffer = &wb->data608->buffer2;
1367 fatal (EXIT_BUG_BUG, "Caption mode has an illegal value at get_writing_buffer(), this is a bug.\n");
1372 void write_char (const unsigned char c, struct s_write *wb)
1374 if (wb->data608->mode!=MODE_TEXT)
1376 struct eia608_screen * use_buffer=get_writing_buffer(wb);
1377 /* hb_log ("\rWriting char [%c] at %s:%d:%d\n",c,
1378 use_buffer == &wb->data608->buffer1?"B1":"B2",
1379 wb->data608->cursor_row,wb->data608->cursor_column); */
1380 use_buffer->characters[wb->data608->cursor_row][wb->data608->cursor_column]=c;
1381 use_buffer->colors[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->color;
1382 use_buffer->fonts[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->font;
1383 use_buffer->row_used[wb->data608->cursor_row]=1;
1384 use_buffer->empty=0;
1385 if (wb->data608->cursor_column<31)
1386 wb->data608->cursor_column++;
1391 /* Handle MID-ROW CODES. */
1392 void handle_text_attr (const unsigned char c1, const unsigned char c2, struct s_write *wb)
1394 // Handle channel change
1395 wb->data608->channel=wb->new_channel;
1396 if (wb->data608->channel!=cc_channel)
1399 hb_log ("\r608: text_attr: %02X %02X",c1,c2);
1400 if ( ((c1!=0x11 && c1!=0x19) ||
1401 (c2<0x20 || c2>0x2f)) && debug_608)
1403 hb_log ("\rThis is not a text attribute!\n");
1408 wb->data608->color=pac2_attribs[i][0];
1409 wb->data608->font=pac2_attribs[i][1];
1411 hb_log (" -- Color: %s, font: %s\n",
1412 color_text[wb->data608->color][0],
1413 font_text[wb->data608->font]);
1414 if (wb->data608->cursor_column<31)
1415 wb->data608->cursor_column++;
1419 void mstotime (LLONG milli, unsigned *hours, unsigned *minutes,
1420 unsigned *seconds, unsigned *ms)
1422 // LLONG milli = (LLONG) ((ccblock*1000)/29.97);
1423 *ms=(unsigned) (milli%1000); // milliseconds
1424 milli=(milli-*ms)/1000; // Remainder, in seconds
1425 *seconds = (int) (milli%60);
1426 milli=(milli-*seconds)/60; // Remainder, in minutes
1427 *minutes = (int) (milli%60);
1428 milli=(milli-*minutes)/60; // Remainder, in hours
1432 void write_subtitle_file_footer (struct s_write *wb)
1434 switch (write_format)
1437 sprintf ((char *) str,"</BODY></SAMI>\n");
1438 if (debug_608 && encoding!=ENC_UNICODE)
1440 hb_log ("\r%s\n", str);
1442 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1443 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1444 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1446 default: // Nothing to do. Only SAMI has a footer
1451 void fhb_log_encoded (FILE *fh, const char *string)
1453 GUARANTEE(strlen (string)*3);
1454 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) string);
1455 fwrite (enc_buffer,enc_buffer_used,1,fh);
1458 void write_subtitle_file_header (struct s_write *wb)
1460 switch (write_format)
1462 case OF_SRT: // Subrip subtitles have no header
1464 case OF_SAMI: // This header brought to you by McPoodle's CCASDI
1465 //fhb_log_encoded (wb->fh, sami_header);
1466 GUARANTEE(strlen (sami_header)*3);
1467 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) sami_header);
1468 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1469 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1471 case OF_RCWT: // Write header
1472 //fwrite (rcwt_header, sizeof(rcwt_header),1,wb->fh);
1474 case OF_TRANSCRIPT: // No header. Fall thru
1480 void write_cc_line_as_transcript (struct eia608_screen *data, struct s_write *wb, int line_number)
1482 hb_buffer_t *buffer;
1486 capitalize(line_number,data, &wb->new_sentence);
1487 correct_case(line_number,data);
1489 int length = get_decoder_line_basic (wb->subline, line_number, data);
1490 if (debug_608 && encoding!=ENC_UNICODE)
1493 hb_log ("%s\n",wb->subline);
1497 //fwrite (wb->subline, 1, length, wb->fh);
1499 * Put this subtitle in a hb_buffer_t and shove it into the subtitle fifo
1501 buffer = hb_buffer_init( length + 1 );
1502 buffer->start = wb->data608->current_visible_start_ms;
1503 buffer->stop = get_fts(wb);
1504 memcpy( buffer->data, wb->subline, length + 1 );
1505 //hb_log("CC %lld: %s", buffer->stop, wb->subline);
1507 if (wb->hb_last_buffer) {
1508 wb->hb_last_buffer->next = buffer;
1510 wb->hb_buffer = buffer;
1512 wb->hb_last_buffer = buffer;
1514 XMLRPC_APPEND(wb->subline,length);
1515 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1516 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1518 // fhb_log (wb->fh,encoded_crlf);
1521 int write_cc_buffer_as_transcript (struct eia608_screen *data, struct s_write *wb)
1525 int wrote_something = 0;
1528 hb_log ("\n- - - TRANSCRIPT caption - - -\n");
1532 if (data->row_used[i])
1534 write_cc_line_as_transcript (data,wb, i);
1540 hb_log ("- - - - - - - - - - - -\r\n");
1542 return wrote_something;
1545 void write_cc_buffer_to_gui (struct eia608_screen *data, struct s_write *wb)
1547 unsigned h1,m1,s1,ms1;
1548 unsigned h2,m2,s2,ms2;
1551 LLONG ms_start= wb->data608->current_visible_start_ms;
1553 ms_start+=subs_delay;
1554 if (ms_start<0) // Drop screens that because of subs_delay start too early
1556 int time_reported=0;
1559 if (data->row_used[i])
1561 hb_log ("###SUBTITLE#");
1564 LLONG ms_end = get_fts(wb)+subs_delay;
1565 mstotime (ms_start,&h1,&m1,&s1,&ms1);
1566 mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
1567 // Note, only MM:SS here as we need to save space in the preview window
1568 hb_log ("%02u:%02u#%02u:%02u#",
1569 h1*60+m1,s1, h2*60+m2,s2);
1575 // We don't capitalize here because whatever function that was used
1576 // before to write to file already took care of it.
1577 int length = get_decoder_line_encoded_for_gui (wb->subline, i, data);
1578 fwrite (wb->subline, 1, length, stderr);
1579 fwrite ("\n",1,1,stderr);
1585 int write_cc_buffer_as_srt (struct eia608_screen *data, struct s_write *wb)
1587 unsigned h1,m1,s1,ms1;
1588 unsigned h2,m2,s2,ms2;
1589 int wrote_something = 0;
1590 LLONG ms_start= wb->data608->current_visible_start_ms;
1593 ms_start+=subs_delay;
1594 if (ms_start<0) // Drop screens that because of subs_delay start too early
1597 LLONG ms_end = get_fts(wb)+subs_delay;
1598 mstotime (ms_start,&h1,&m1,&s1,&ms1);
1599 mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
1601 wb->data608->srt_counter++;
1602 sprintf (timeline,"%u\r\n",wb->data608->srt_counter);
1603 //enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline);
1604 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1605 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1606 //sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n",
1607 // h1,m1,s1,ms1, h2,m2,s2,ms2);
1608 //enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline);
1611 hb_log ("\n- - - SRT caption - - -\n");
1614 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1615 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1618 if (data->row_used[i])
1622 capitalize(i,data, &wb->new_sentence);
1623 correct_case(i,data);
1625 int length = get_decoder_line_encoded (wb->subline, i, data);
1626 if (debug_608 && encoding!=ENC_UNICODE)
1629 hb_log ("%s\n",wb->subline);
1633 hb_buffer_t *buffer = hb_buffer_init( length + 1 );
1634 buffer->start = ms_start;
1635 buffer->stop = ms_end;
1636 memcpy( buffer->data, wb->subline, length + 1 );
1638 if (wb->hb_last_buffer) {
1639 wb->hb_last_buffer->next = buffer;
1641 wb->hb_buffer = buffer;
1643 wb->hb_last_buffer = buffer;
1645 //fwrite (wb->subline, 1, length, wb->fh);
1646 XMLRPC_APPEND(wb->subline,length);
1647 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1648 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1650 // fhb_log (wb->fh,encoded_crlf);
1656 hb_log ("- - - - - - - - - - - -\r\n");
1658 // fhb_log (wb->fh, encoded_crlf);
1659 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1660 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1661 return wrote_something;
1664 int write_cc_buffer_as_sami (struct eia608_screen *data, struct s_write *wb)
1666 int wrote_something=0;
1667 LLONG startms = wb->data608->current_visible_start_ms;
1670 startms+=subs_delay;
1671 if (startms<0) // Drop screens that because of subs_delay start too early
1674 LLONG endms = get_fts(wb)+subs_delay;
1675 endms--; // To prevent overlapping with next line.
1676 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\">\r\n",startms);
1677 if (debug_608 && encoding!=ENC_UNICODE)
1679 hb_log ("\r%s\n", str);
1681 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1682 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1683 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1686 if (data->row_used[i])
1688 int length = get_decoder_line_encoded (wb->subline, i, data);
1689 if (debug_608 && encoding!=ENC_UNICODE)
1692 hb_log ("%s\n",wb->subline);
1694 fwrite (wb->subline, 1, length, wb->fh);
1695 XMLRPC_APPEND(wb->subline,length);
1699 fwrite (encoded_br, 1, encoded_br_length,wb->fh);
1700 XMLRPC_APPEND(encoded_br, encoded_br_length);
1702 fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1703 XMLRPC_APPEND(encoded_crlf, encoded_crlf_length);
1706 sprintf ((char *) str,"</P></SYNC>\r\n");
1707 if (debug_608 && encoding!=ENC_UNICODE)
1709 hb_log ("\r%s\n", str);
1711 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1712 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1713 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1714 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",endms);
1715 if (debug_608 && encoding!=ENC_UNICODE)
1717 hb_log ("\r%s\n", str);
1719 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1720 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1721 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1722 return wrote_something;
1725 struct eia608_screen *get_current_visible_buffer (struct s_write *wb)
1727 struct eia608_screen *data;
1728 if (wb->data608->visible_buffer==1)
1729 data = &wb->data608->buffer1;
1731 data = &wb->data608->buffer2;
1736 int write_cc_buffer (struct s_write *wb)
1738 struct eia608_screen *data;
1739 int wrote_something=0;
1740 if (screens_to_process!=-1 && wb->data608->screenfuls_counter>=screens_to_process)
1746 if (wb->data608->visible_buffer==1)
1747 data = &wb->data608->buffer1;
1749 data = &wb->data608->buffer2;
1754 switch (write_format)
1757 wrote_something = write_cc_buffer_as_srt (data, wb);
1760 wrote_something = write_cc_buffer_as_sami (data,wb);
1763 wrote_something = write_cc_buffer_as_transcript (data,wb);
1768 if (wrote_something && gui_mode_reports)
1769 write_cc_buffer_to_gui (data,wb);
1771 return wrote_something;
1774 void roll_up(struct s_write *wb)
1776 struct eia608_screen *use_buffer;
1779 if (wb->data608->visible_buffer==1)
1780 use_buffer = &wb->data608->buffer1;
1782 use_buffer = &wb->data608->buffer2;
1784 switch (wb->data608->mode)
1795 default: // Shouldn't happen
1799 int firstrow=-1, lastrow=-1;
1800 // Look for the last line used
1801 int rows_now=0; // Number of rows in use right now
1804 if (use_buffer->row_used[i])
1814 hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow);
1816 if (lastrow==-1) // Empty screen, nothing to rollup
1819 for (j=lastrow-keep_lines+1;j<lastrow; j++)
1823 memcpy (use_buffer->characters[j],use_buffer->characters[j+1],CC608_SCREEN_WIDTH+1);
1824 memcpy (use_buffer->colors[j],use_buffer->colors[j+1],CC608_SCREEN_WIDTH+1);
1825 memcpy (use_buffer->fonts[j],use_buffer->fonts[j+1],CC608_SCREEN_WIDTH+1);
1826 use_buffer->row_used[j]=use_buffer->row_used[j+1];
1829 for (j=0;j<(1+wb->data608->cursor_row-keep_lines);j++)
1831 memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH);
1832 memset(use_buffer->colors[j],COL_WHITE,CC608_SCREEN_WIDTH);
1833 memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH);
1834 use_buffer->characters[j][CC608_SCREEN_WIDTH]=0;
1835 use_buffer->row_used[j]=0;
1837 memset(use_buffer->characters[lastrow],' ',CC608_SCREEN_WIDTH);
1838 memset(use_buffer->colors[lastrow],COL_WHITE,CC608_SCREEN_WIDTH);
1839 memset(use_buffer->fonts[lastrow],FONT_REGULAR,CC608_SCREEN_WIDTH);
1841 use_buffer->characters[lastrow][CC608_SCREEN_WIDTH]=0;
1842 use_buffer->row_used[lastrow]=0;
1847 if (use_buffer->row_used[i])
1849 if (rows_now>keep_lines)
1850 hb_log ("Bug in roll_up, should have %d lines but I have %d.\n",
1851 keep_lines, rows_now);
1854 void erase_memory (struct s_write *wb, int displayed)
1856 struct eia608_screen *buf;
1859 if (wb->data608->visible_buffer==1)
1860 buf=&wb->data608->buffer1;
1862 buf=&wb->data608->buffer2;
1866 if (wb->data608->visible_buffer==1)
1867 buf=&wb->data608->buffer2;
1869 buf=&wb->data608->buffer1;
1871 clear_eia608_cc_buffer (buf);
1874 int is_current_row_empty (struct s_write *wb)
1876 struct eia608_screen *use_buffer;
1879 if (wb->data608->visible_buffer==1)
1880 use_buffer = &wb->data608->buffer1;
1882 use_buffer = &wb->data608->buffer2;
1883 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1885 if (use_buffer->characters[wb->data608->rollup_base_row][i]!=' ')
1891 /* Process GLOBAL CODES */
1892 void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct s_write *wb)
1894 // Handle channel change
1895 wb->data608->channel=wb->new_channel;
1896 if (wb->data608->channel!=cc_channel)
1899 enum command_code command = COM_UNKNOWN;
1902 if ((c1==0x14 || c1==0x1C) && c2==0x2C)
1903 command = COM_ERASEDISPLAYEDMEMORY;
1904 if ((c1==0x14 || c1==0x1C) && c2==0x20)
1905 command = COM_RESUMECAPTIONLOADING;
1906 if ((c1==0x14 || c1==0x1C) && c2==0x2F)
1907 command = COM_ENDOFCAPTION;
1908 if ((c1==0x17 || c1==0x1F) && c2==0x21)
1909 command = COM_TABOFFSET1;
1910 if ((c1==0x17 || c1==0x1F) && c2==0x22)
1911 command = COM_TABOFFSET2;
1912 if ((c1==0x17 || c1==0x1F) && c2==0x23)
1913 command = COM_TABOFFSET3;
1914 if ((c1==0x14 || c1==0x1C) && c2==0x25)
1915 command = COM_ROLLUP2;
1916 if ((c1==0x14 || c1==0x1C) && c2==0x26)
1917 command = COM_ROLLUP3;
1918 if ((c1==0x14 || c1==0x1C) && c2==0x27)
1919 command = COM_ROLLUP4;
1920 if ((c1==0x14 || c1==0x1C) && c2==0x2D)
1921 command = COM_CARRIAGERETURN;
1922 if ((c1==0x14 || c1==0x1C) && c2==0x2E)
1923 command = COM_ERASENONDISPLAYEDMEMORY;
1924 if ((c1==0x14 || c1==0x1C) && c2==0x21)
1925 command = COM_BACKSPACE;
1926 if ((c1==0x14 || c1==0x1C) && c2==0x2b)
1927 command = COM_RESUMETEXTDISPLAY;
1930 hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]);
1935 if (wb->data608->cursor_column>0)
1937 wb->data608->cursor_column--;
1938 get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' ';
1941 case COM_TABOFFSET1:
1942 if (wb->data608->cursor_column<31)
1943 wb->data608->cursor_column++;
1945 case COM_TABOFFSET2:
1946 wb->data608->cursor_column+=2;
1947 if (wb->data608->cursor_column>31)
1948 wb->data608->cursor_column=31;
1950 case COM_TABOFFSET3:
1951 wb->data608->cursor_column+=3;
1952 if (wb->data608->cursor_column>31)
1953 wb->data608->cursor_column=31;
1955 case COM_RESUMECAPTIONLOADING:
1956 wb->data608->mode=MODE_POPUP;
1958 case COM_RESUMETEXTDISPLAY:
1959 wb->data608->mode=MODE_TEXT;
1962 if (wb->data608->mode==MODE_POPUP)
1964 if (write_cc_buffer (wb))
1965 wb->data608->screenfuls_counter++;
1966 erase_memory (wb, 1);
1968 if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb))
1971 hb_log ("Two RU2, current line not empty. Simulating a CR\n");
1972 handle_command(0x14, 0x2D, wb);
1974 wb->data608->mode=MODE_ROLLUP_2;
1975 erase_memory (wb, 0);
1976 wb->data608->cursor_column=0;
1977 wb->data608->cursor_row=wb->data608->rollup_base_row;
1980 if (wb->data608->mode==MODE_POPUP)
1982 if (write_cc_buffer (wb))
1983 wb->data608->screenfuls_counter++;
1984 erase_memory (wb, 1);
1986 if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb))
1989 hb_log ("Two RU3, current line not empty. Simulating a CR\n");
1990 handle_command(0x14, 0x2D, wb);
1992 wb->data608->mode=MODE_ROLLUP_3;
1993 erase_memory (wb, 0);
1994 wb->data608->cursor_column=0;
1995 wb->data608->cursor_row=wb->data608->rollup_base_row;
1998 if (wb->data608->mode==MODE_POPUP)
2000 if (write_cc_buffer (wb))
2001 wb->data608->screenfuls_counter++;
2002 erase_memory (wb, 1);
2004 if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb))
2007 hb_log ("Two RU4, current line not empty. Simulating a CR\n");
2008 handle_command(0x14, 0x2D, wb);
2011 wb->data608->mode=MODE_ROLLUP_4;
2012 wb->data608->cursor_column=0;
2013 wb->data608->cursor_row=wb->data608->rollup_base_row;
2014 erase_memory (wb, 0);
2016 case COM_CARRIAGERETURN:
2017 // In transcript mode, CR doesn't write the whole screen, to avoid
2019 if (write_format==OF_TRANSCRIPT)
2021 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2026 delete_all_lines_but_current (get_current_visible_buffer (wb), wb->data608->cursor_row);
2027 if (write_cc_buffer(wb))
2028 wb->data608->screenfuls_counter++;
2031 wb->data608->current_visible_start_ms=get_fts(wb);
2032 wb->data608->cursor_column=0;
2034 case COM_ERASENONDISPLAYEDMEMORY:
2035 erase_memory (wb,0);
2037 case COM_ERASEDISPLAYEDMEMORY:
2038 // Write it to disk before doing this, and make a note of the new
2039 // time it became clear.
2040 if (write_format==OF_TRANSCRIPT &&
2041 (wb->data608->mode==MODE_ROLLUP_2 || wb->data608->mode==MODE_ROLLUP_3 ||
2042 wb->data608->mode==MODE_ROLLUP_4))
2044 // In transcript mode we just write the cursor line. The previous lines
2045 // should have been written already, so writing everything produces
2047 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2051 if (write_cc_buffer (wb))
2052 wb->data608->screenfuls_counter++;
2054 erase_memory (wb,1);
2055 wb->data608->current_visible_start_ms=get_fts(wb);
2057 case COM_ENDOFCAPTION: // Switch buffers
2058 // The currently *visible* buffer is leaving, so now we know it's ending
2059 // time. Time to actually write it to file.
2060 if (write_cc_buffer (wb))
2061 wb->data608->screenfuls_counter++;
2062 wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1;
2063 wb->data608->current_visible_start_ms=get_fts(wb);
2064 wb->data608->cursor_column=0;
2065 wb->data608->cursor_row=0;
2066 wb->data608->color=default_color;
2067 wb->data608->font=FONT_REGULAR;
2072 hb_log ("\rNot yet implemented.\n");
2078 void handle_end_of_data (struct s_write *wb)
2080 // We issue a EraseDisplayedMemory here so if there's any captions pending
2081 // they get written to file.
2082 handle_command (0x14, 0x2c, wb); // EDM
2085 void handle_double (const unsigned char c1, const unsigned char c2, struct s_write *wb)
2088 if (wb->data608->channel!=cc_channel)
2090 if (c2>=0x30 && c2<=0x3f)
2092 c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here
2094 hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c);
2099 /* Process EXTENDED CHARACTERS */
2100 unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_write *wb)
2102 // Handle channel change
2103 if (wb->new_channel > 2)
2105 wb->new_channel -= 2;
2107 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2109 wb->data608->channel=wb->new_channel;
2110 if (wb->data608->channel!=cc_channel)
2113 // For lo values between 0x20-0x3f
2117 hb_log ("\rExtended: %02X %02X\n",hi,lo);
2118 if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13))
2123 c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here
2126 c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here
2129 // This column change is because extended characters replace
2130 // the previous character (which is sent for basic decoders
2131 // to show something similar to the real char)
2132 if (wb->data608->cursor_column>0)
2133 wb->data608->cursor_column--;
2140 /* Process PREAMBLE ACCESS CODES (PAC) */
2141 void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb)
2143 // Handle channel change
2144 if (wb->new_channel > 2)
2146 wb->new_channel -= 2;
2148 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2150 wb->data608->channel=wb->new_channel;
2151 if (wb->data608->channel!=cc_channel)
2154 int row=rowdata[((c1<<1)&14)|((c2>>5)&1)];
2157 hb_log ("\rPAC: %02X %02X",c1,c2);
2159 if (c2>=0x40 && c2<=0x5f)
2165 if (c2>=0x60 && c2<=0x7f)
2172 hb_log ("\rThis is not a PAC!!!!!\n");
2176 int color=pac2_attribs[c2][0];
2177 int font=pac2_attribs[c2][1];
2178 int indent=pac2_attribs[c2][2];
2180 hb_log (" -- Position: %d:%d, color: %s, font: %s\n",row,
2181 indent,color_text[color][0],font_text[font]);
2182 if (wb->data608->mode!=MODE_TEXT)
2184 // According to Robson, row info is discarded in text mode
2185 // but column is accepted
2186 wb->data608->cursor_row=row-1 ; // Since the array is 0 based
2188 wb->data608->rollup_base_row=row-1;
2189 wb->data608->cursor_column=indent;
2193 void handle_single (const unsigned char c1, struct s_write *wb)
2195 if (c1<0x20 || wb->data608->channel!=cc_channel)
2196 return; // We don't allow special stuff here
2201 hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */
2205 int check_channel (unsigned char c1, struct s_write *wb)
2209 if (debug_608 && wb->data608->channel!=1)
2210 hb_log ("\nChannel change, now 1\n");
2215 if (debug_608 && wb->data608->channel!=2)
2216 hb_log ("\nChannel change, now 2\n");
2221 if (debug_608 && wb->data608->channel!=3)
2222 hb_log ("\nChannel change, now 3\n");
2227 if (debug_608 && wb->data608->channel!=4)
2228 hb_log ("\nChannel change, now 4\n");
2232 // Otherwise keep the current channel
2233 return wb->data608->channel;
2236 /* Handle Command, special char or attribute and also check for
2238 * Returns 1 if something was written to screen, 0 otherwise */
2239 int disCommand (unsigned char hi, unsigned char lo, struct s_write *wb)
2241 int wrote_to_screen=0;
2243 /* Full channel changes are only allowed for "GLOBAL CODES",
2244 * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES",
2246 * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and
2247 * SPECIAL/SPECIAL CHARACTERS allow only switching
2248 * between 1&3 or 2&4. */
2249 wb->new_channel = check_channel (hi,wb);
2250 //if (wb->data608->channel!=cc_channel)
2253 if (hi>=0x18 && hi<=0x1f)
2259 if (lo>=0x40 && lo<=0x5f)
2260 handle_pac (hi,lo,wb);
2263 if (lo>=0x20 && lo<=0x2f)
2264 handle_text_attr (hi,lo,wb);
2265 if (lo>=0x30 && lo<=0x3f)
2268 handle_double (hi,lo,wb);
2270 if (lo>=0x40 && lo<=0x7f)
2271 handle_pac (hi,lo,wb);
2275 if (lo>=0x20 && lo<=0x3f)
2277 wrote_to_screen=handle_extended (hi,lo,wb);
2279 if (lo>=0x40 && lo<=0x7f)
2280 handle_pac (hi,lo,wb);
2284 if (lo>=0x20 && lo<=0x2f)
2285 handle_command (hi,lo,wb);
2286 if (lo>=0x40 && lo<=0x7f)
2287 handle_pac (hi,lo,wb);
2290 if (lo>=0x40 && lo<=0x7f)
2291 handle_pac (hi,lo,wb);
2294 if (lo>=0x21 && lo<=0x22)
2295 handle_command (hi,lo,wb);
2296 if (lo>=0x2e && lo<=0x2f)
2297 handle_text_attr (hi,lo,wb);
2298 if (lo>=0x40 && lo<=0x7f)
2299 handle_pac (hi,lo,wb);
2302 return wrote_to_screen;
2305 void process608 (const unsigned char *data, int length, struct s_write *wb)
2307 static int textprinted = 0;
2312 for (i=0;i<length;i=i+2)
2314 unsigned char hi, lo;
2315 int wrote_to_screen=0;
2316 hi = data[i] & 0x7F; // Get rid of parity bit
2317 lo = data[i+1] & 0x7F; // Get rid of parity bit
2319 if (hi==0 && lo==0) // Just padding
2321 // hb_log ("\r[%02X:%02X]\n",hi,lo);
2323 if (hi>=0x01 && hi<=0x0E)
2325 // XDS crap - mode. Would be nice to support it eventually
2326 // wb->data608->last_c1=0;
2327 // wb->data608->last_c2=0;
2328 wb->data608->channel=3; // Always channel 3
2331 if (hi==0x0F) // End of XDS block
2336 if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char
2337 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
2338 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
2340 // We were writing characters before, start a new line for
2341 // diagnostic output from disCommand()
2342 if (debug_608 && textprinted == 1 )
2348 wb->in_xds_mode=0; // Back to normal
2349 if (wb->data608->last_c1==hi && wb->data608->last_c2==lo)
2351 // Duplicate dual code, discard
2354 wb->data608->last_c1=hi;
2355 wb->data608->last_c2=lo;
2356 wrote_to_screen=disCommand (hi,lo,wb);
2358 if (hi>=0x20) // Standard characters (always in pairs)
2360 // Only print if the channel is active
2361 if (wb->data608->channel!=cc_channel)
2366 if( textprinted == 0 )
2373 handle_single(hi,wb);
2374 handle_single(lo,wb);
2376 wb->data608->last_c1=0;
2377 wb->data608->last_c2=0;
2380 if ( debug_608 && !textprinted && wb->data608->channel==cc_channel )
2381 { // Current FTS information after the characters are shown
2382 //hb_log("Current FTS: %s\n", print_mstime(get_fts()));
2385 if (wrote_to_screen && direct_rollup && // If direct_rollup is enabled and
2386 (wb->data608->mode==MODE_ROLLUP_2 || // we are in rollup mode, write now.
2387 wb->data608->mode==MODE_ROLLUP_3 ||
2388 wb->data608->mode==MODE_ROLLUP_4))
2390 // We don't increase screenfuls_counter here.
2391 write_cc_buffer (wb);
2392 wb->data608->current_visible_start_ms=get_fts(wb);
2399 /* Return a pointer to a string that holds the printable characters
2400 * of the caption data block. FOR DEBUG PURPOSES ONLY! */
2401 unsigned char *debug_608toASC (unsigned char *cc_data, int channel)
2403 static unsigned char output[3];
2405 unsigned char cc_valid = (cc_data[0] & 4) >>2;
2406 unsigned char cc_type = cc_data[0] & 3;
2407 unsigned char hi, lo;
2413 if (cc_valid && cc_type==channel)
2415 hi = cc_data[1] & 0x7F; // Get rid of parity bit
2416 lo = cc_data[2] & 0x7F; // Get rid of parity bit
2420 output[1]=(lo>=20 ? lo : '.');
2434 struct hb_work_private_s
2437 struct s_write * cc608;
2440 int decccInit( hb_work_object_t * w, hb_job_t * job )
2443 hb_work_private_t * pv;
2445 pv = calloc( 1, sizeof( hb_work_private_t ) );
2448 w->private_data = pv;
2452 pv->cc608 = calloc(1, sizeof(struct s_write));
2456 retval = general_608_init(pv->cc608);
2459 pv->cc608->data608 = calloc(1, sizeof(struct eia608));
2460 if( !pv->cc608->data608 )
2470 int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
2471 hb_buffer_t ** buf_out )
2473 hb_work_private_t * pv = w->private_data;
2474 hb_buffer_t * in = *buf_in;
2476 if ( in->size <= 0 )
2478 /* EOF on input stream - send it downstream & say that we're done */
2479 handle_end_of_data(pv->cc608);
2481 * Grab any pending buffer and output them with the EOF on the end
2483 if (pv->cc608->hb_last_buffer) {
2484 pv->cc608->hb_last_buffer->next = in;
2485 *buf_out = pv->cc608->hb_buffer;
2487 pv->cc608->hb_buffer = NULL;
2488 pv->cc608->hb_last_buffer = NULL;
2493 return HB_WORK_DONE;
2496 pv->cc608->last_pts = in->start;
2498 process608(in->data, in->size, pv->cc608);
2501 * If there is one waiting then pass it on
2503 *buf_out = pv->cc608->hb_buffer;
2504 pv->cc608->hb_buffer = NULL;
2505 pv->cc608->hb_last_buffer = NULL;
2510 void decccClose( hb_work_object_t * w )
2512 hb_work_private_t * pv = w->private_data;
2513 general_608_close( pv->cc608 );
2514 free( pv->cc608->data608 );
2516 free( w->private_data );
2519 hb_work_object_t hb_deccc608 =
2522 "Closed Caption (608) decoder",