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 = 1;
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 = 0;
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;
28 * Get the time of the last buffer that we have received.
30 static LLONG get_fts(struct s_write *wb)
35 #define fatal(N, ...) // N
36 #define XMLRPC_APPEND(N, ...) // N
38 int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10};
39 // Relationship between the first PAC byte and the row number
41 // The following enc_buffer is not used at the moment, if it does get used
42 // we need to bring it into the swrite struct. Same for "str".
43 #define INITIAL_ENC_BUFFER_CAPACITY 2048
45 unsigned char str[2048]; // Another generic general purpose buffer
47 #define GUARANTEE(wb, length) if (length>wb->enc_buffer_capacity) \
48 {wb->enc_buffer_capacity*=2; wb->enc_buffer=(unsigned char*) realloc (wb->enc_buffer, wb->enc_buffer_capacity); \
49 if (wb->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)
156 if( !wb->enc_buffer )
158 wb->enc_buffer=(unsigned char *) malloc (INITIAL_ENC_BUFFER_CAPACITY);
159 if (wb->enc_buffer==NULL)
161 wb->enc_buffer_capacity=INITIAL_ENC_BUFFER_CAPACITY;
165 wb->subline = malloc(2048);
173 wb->new_sentence = 1;
177 wb->hb_buffer = NULL;
178 wb->hb_last_buffer = NULL;
184 * Free up CC memory - don't call this from HB just yet since it will cause
185 * parallel encodes to fail - to be honest they will be stuffed anyway since
186 * the CC's may be overwriting the buffers.
188 void general_608_close (struct s_write *wb)
190 if( wb->enc_buffer ) {
191 free(wb->enc_buffer);
192 wb->enc_buffer_capacity = 0;
193 wb->enc_buffer_used = 0;
199 if( wb->hb_buffer ) {
200 hb_buffer_close( &wb->hb_buffer );
207 void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
209 unsigned char c1='?';
212 // Regular line-21 character set, mostly ASCII except these exceptions
215 case 0x2a: // lowercase a, acute accent
218 case 0x5c: // lowercase e, acute accent
221 case 0x5e: // lowercase i, acute accent
224 case 0x5f: // lowercase o, acute accent
227 case 0x60: // lowercase u, acute accent
230 case 0x7b: // lowercase c with cedilla
233 case 0x7c: // division symbol
236 case 0x7d: // uppercase N tilde
239 case 0x7e: // lowercase n tilde
251 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
252 // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
253 case 0x80: // Registered symbol (R)
256 case 0x81: // degree sign
259 case 0x82: // 1/2 symbol
262 case 0x83: // Inverted (open) question mark
265 case 0x84: // Trademark symbol (TM) - Does not exist in Latin 1
267 case 0x85: // Cents symbol
270 case 0x86: // Pounds sterling
273 case 0x87: // Music note - Not in latin 1, so we use 'pilcrow'
276 case 0x88: // lowercase a, grave accent
279 case 0x89: // transparent space, we make it regular
282 case 0x8a: // lowercase e, grave accent
285 case 0x8b: // lowercase a, circumflex accent
288 case 0x8c: // lowercase e, circumflex accent
291 case 0x8d: // lowercase i, circumflex accent
294 case 0x8e: // lowercase o, circumflex accent
297 case 0x8f: // lowercase u, circumflex accent
300 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
301 // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
302 case 0x90: // capital letter A with acute
305 case 0x91: // capital letter E with acute
308 case 0x92: // capital letter O with acute
311 case 0x93: // capital letter U with acute
314 case 0x94: // capital letter U with diaresis
317 case 0x95: // lowercase letter U with diaeresis
320 case 0x96: // apostrophe
323 case 0x97: // inverted exclamation mark
326 case 0x98: // asterisk
329 case 0x99: // apostrophe (yes, duped). See CCADI source code.
332 case 0x9a: // hyphen-minus
335 case 0x9b: // copyright sign
338 case 0x9c: // Service Mark - not available in latin 1
340 case 0x9d: // Full stop (.)
343 case 0x9e: // Quoatation mark
346 case 0x9f: // Quoatation mark
349 case 0xa0: // uppercase A, grave accent
352 case 0xa1: // uppercase A, circumflex
355 case 0xa2: // uppercase C with cedilla
358 case 0xa3: // uppercase E, grave accent
361 case 0xa4: // uppercase E, circumflex
364 case 0xa5: // capital letter E with diaresis
367 case 0xa6: // lowercase letter e with diaresis
370 case 0xa7: // uppercase I, circumflex
373 case 0xa8: // uppercase I, with diaresis
376 case 0xa9: // lowercase i, with diaresis
379 case 0xaa: // uppercase O, circumflex
382 case 0xab: // uppercase U, grave accent
385 case 0xac: // lowercase u, grave accent
388 case 0xad: // uppercase U, circumflex
391 case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
394 case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
397 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
398 // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
399 case 0xb0: // Uppercase A, tilde
402 case 0xb1: // Lowercase a, tilde
405 case 0xb2: // Uppercase I, acute accent
408 case 0xb3: // Uppercase I, grave accent
411 case 0xb4: // Lowercase i, grave accent
414 case 0xb5: // Uppercase O, grave accent
417 case 0xb6: // Lowercase o, grave accent
420 case 0xb7: // Uppercase O, tilde
423 case 0xb8: // Lowercase o, tilde
426 case 0xb9: // Open curly brace
429 case 0xba: // Closing curly brace
432 case 0xbb: // Backslash
438 case 0xbd: // Underscore
441 case 0xbe: // Pipe (broken bar)
447 case 0xc0: // Uppercase A, umlaut
450 case 0xc1: // Lowercase A, umlaut
453 case 0xc2: // Uppercase O, umlaut
456 case 0xc3: // Lowercase o, umlaut
459 case 0xc4: // Esszett (sharp S)
462 case 0xc5: // Yen symbol
465 case 0xc6: // Currency symbol
468 case 0xc7: // Vertical bar
471 case 0xc8: // Uppercase A, ring
474 case 0xc9: // Lowercase A, ring
477 case 0xca: // Uppercase O, slash
480 case 0xcb: // Lowercase o, slash
483 case 0xcc: // Upper left corner
484 case 0xcd: // Upper right corner
485 case 0xce: // Lower left corner
486 case 0xcf: // Lower right corner
487 default: // For those that don't have representation
488 *buffer='?'; // I'll do it eventually, I promise
489 break; // This are weird chars anyway
494 void get_char_in_unicode (unsigned char *buffer, unsigned char c)
499 case 0x84: // Trademark symbol (TM)
503 case 0x87: // Music note
507 case 0x9c: // Service Mark
511 case 0xcc: // Upper left corner
515 case 0xcd: // Upper right corner
519 case 0xce: // Lower left corner
523 case 0xcf: // Lower right corner
527 default: // Everything else, same as latin-1 followed by 00
528 get_char_in_latin_1 (&c1,c);
536 int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number of bytes used
540 if (c<0x80) // Regular line-21 character set, mostly ASCII except these exceptions
544 case 0x2a: // lowercase a, acute accent
548 case 0x5c: // lowercase e, acute accent
552 case 0x5e: // lowercase i, acute accent
556 case 0x5f: // lowercase o, acute accent
560 case 0x60: // lowercase u, acute accent
564 case 0x7b: // lowercase c with cedilla
568 case 0x7c: // division symbol
572 case 0x7d: // uppercase N tilde
576 case 0x7e: // lowercase n tilde
587 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
588 // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
589 case 0x80: // Registered symbol (R)
593 case 0x81: // degree sign
597 case 0x82: // 1/2 symbol
601 case 0x83: // Inverted (open) question mark
605 case 0x84: // Trademark symbol (TM)
610 case 0x85: // Cents symbol
614 case 0x86: // Pounds sterling
618 case 0x87: // Music note
623 case 0x88: // lowercase a, grave accent
627 case 0x89: // transparent space, we make it regular
630 case 0x8a: // lowercase e, grave accent
634 case 0x8b: // lowercase a, circumflex accent
638 case 0x8c: // lowercase e, circumflex accent
642 case 0x8d: // lowercase i, circumflex accent
646 case 0x8e: // lowercase o, circumflex accent
650 case 0x8f: // lowercase u, circumflex accent
654 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
655 // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
656 case 0x90: // capital letter A with acute
660 case 0x91: // capital letter E with acute
664 case 0x92: // capital letter O with acute
668 case 0x93: // capital letter U with acute
672 case 0x94: // capital letter U with diaresis
676 case 0x95: // lowercase letter U with diaeresis
680 case 0x96: // apostrophe
683 case 0x97: // inverted exclamation mark
687 case 0x98: // asterisk
690 case 0x99: // apostrophe (yes, duped). See CCADI source code.
693 case 0x9a: // hyphen-minus
696 case 0x9b: // copyright sign
700 case 0x9c: // Service mark
705 case 0x9d: // Full stop (.)
708 case 0x9e: // Quoatation mark
711 case 0x9f: // Quoatation mark
714 case 0xa0: // uppercase A, grave accent
718 case 0xa1: // uppercase A, circumflex
722 case 0xa2: // uppercase C with cedilla
726 case 0xa3: // uppercase E, grave accent
730 case 0xa4: // uppercase E, circumflex
734 case 0xa5: // capital letter E with diaresis
738 case 0xa6: // lowercase letter e with diaresis
742 case 0xa7: // uppercase I, circumflex
746 case 0xa8: // uppercase I, with diaresis
750 case 0xa9: // lowercase i, with diaresis
754 case 0xaa: // uppercase O, circumflex
758 case 0xab: // uppercase U, grave accent
762 case 0xac: // lowercase u, grave accent
766 case 0xad: // uppercase U, circumflex
770 case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
774 case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
778 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
779 // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
780 case 0xb0: // Uppercase A, tilde
784 case 0xb1: // Lowercase a, tilde
788 case 0xb2: // Uppercase I, acute accent
792 case 0xb3: // Uppercase I, grave accent
796 case 0xb4: // Lowercase i, grave accent
800 case 0xb5: // Uppercase O, grave accent
804 case 0xb6: // Lowercase o, grave accent
808 case 0xb7: // Uppercase O, tilde
812 case 0xb8: // Lowercase o, tilde
816 case 0xb9: // Open curly brace
819 case 0xba: // Closing curly brace
822 case 0xbb: // Backslash
828 case 0xbd: // Underscore
831 case 0xbe: // Pipe (broken bar)
836 *buffer=0x7e; // Not sure
838 case 0xc0: // Uppercase A, umlaut
842 case 0xc1: // Lowercase A, umlaut
846 case 0xc2: // Uppercase O, umlaut
850 case 0xc3: // Lowercase o, umlaut
854 case 0xc4: // Esszett (sharp S)
858 case 0xc5: // Yen symbol
862 case 0xc6: // Currency symbol
866 case 0xc7: // Vertical bar
869 case 0xc8: // Uppercase A, ring
873 case 0xc9: // Lowercase A, ring
877 case 0xca: // Uppercase O, slash
881 case 0xcb: // Lowercase o, slash
885 case 0xcc: // Upper left corner
890 case 0xcd: // Upper right corner
895 case 0xce: // Lower left corner
900 case 0xcf: // Lower right corner
906 *buffer='?'; // I'll do it eventually, I promise
907 return 1; // This are weird chars anyway
911 unsigned char cctolower (unsigned char c)
913 if (c>='A' && c<='Z')
917 case 0x7d: // uppercase N tilde
919 case 0x90: // capital letter A with acute
921 case 0x91: // capital letter E with acute
923 case 0x92: // capital letter O with acute
925 case 0x93: // capital letter U with acute
927 case 0xa2: // uppercase C with cedilla
929 case 0xa0: // uppercase A, grave accent
931 case 0xa3: // uppercase E, grave accent
933 case 0xa1: // uppercase A, circumflex
935 case 0xa4: // uppercase E, circumflex
937 case 0xa7: // uppercase I, circumflex
939 case 0xaa: // uppercase O, circumflex
941 case 0xad: // uppercase U, circumflex
943 case 0x94: // capital letter U with diaresis
945 case 0xa5: // capital letter E with diaresis
947 case 0xa8: // uppercase I, with diaresis
949 case 0xab: // uppercase U, grave accent
951 case 0xb0: // Uppercase A, tilde
953 case 0xb2: // Uppercase I, acute accent
955 case 0xb3: // Uppercase I, grave accent
957 case 0xb5: // Uppercase O, grave accent
959 case 0xb7: // Uppercase O, tilde
961 case 0xc0: // Uppercase A, umlaut
963 case 0xc2: // Uppercase O, umlaut
965 case 0xc8: // Uppercase A, ring
967 case 0xca: // Uppercase O, slash
973 unsigned char cctoupper (unsigned char c)
975 if (c>='a' && c<='z')
979 case 0x7e: // lowercase n tilde
981 case 0x2a: // lowercase a, acute accent
983 case 0x5c: // lowercase e, acute accent
985 case 0x5e: // lowercase i, acute accent
987 case 0x5f: // lowercase o, acute accent
989 case 0x60: // lowercase u, acute accent
991 case 0x7b: // lowercase c with cedilla
993 case 0x88: // lowercase a, grave accent
995 case 0x8a: // lowercase e, grave accent
997 case 0x8b: // lowercase a, circumflex accent
999 case 0x8c: // lowercase e, circumflex accent
1001 case 0x8d: // lowercase i, circumflex accent
1003 case 0x8e: // lowercase o, circumflex accent
1005 case 0x8f: // lowercase u, circumflex accent
1007 case 0x95: // lowercase letter U with diaeresis
1009 case 0xa6: // lowercase letter e with diaresis
1011 case 0xa9: // lowercase i, with diaresis
1013 case 0xac: // lowercase u, grave accent
1015 case 0xb1: // Lowercase a, tilde
1017 case 0xb4: // Lowercase i, grave accent
1019 case 0xb6: // Lowercase o, grave accent
1021 case 0xb8: // Lowercase o, tilde
1023 case 0xc1: // Lowercase A, umlaut
1025 case 0xc3: // Lowercase o, umlaut
1027 case 0xc9: // Lowercase A, ring
1029 case 0xcb: // Lowercase o, slash
1036 // Encodes a generic string. Note that since we use the encoders for closed caption
1037 // data, text would have to be encoded as CCs... so using special characters here
1039 unsigned encode_line (unsigned char *buffer, unsigned char *text)
1064 #define ISSEPARATOR(c) (c==' ' || c==0x89 || ispunct(c) \
1065 || c==0x99) // This is the apostrofe. We get it here in CC encoding, not ASCII
1068 void correct_case (int line_num, struct eia608_screen *data)
1071 /* while (i<spell_words) */
1073 /* char *c=(char *) data->characters[line_num]; */
1074 /* size_t len=strlen (spell_correct[i]); */
1075 /* while ((c=strstr (c,spell_lower[i]))!=NULL) */
1077 /* // Make sure it's a whole word (start of line or */
1078 /* // preceded by space, and end of line or followed by */
1080 /* unsigned char prev; */
1081 /* if (c==(char *) data->characters[line_num]) // Beginning of line... */
1082 /* prev=' '; // ...Pretend we had a blank before */
1085 /* unsigned char next; */
1086 /* if (c-(char *) data->characters[line_num]+len==CC608_SCREEN_WIDTH) // End of line... */
1087 /* next=' '; // ... pretend we have a blank later */
1089 /* next=*(c+len); */
1090 /* if ( ISSEPARATOR(prev) && ISSEPARATOR(next)) */
1092 /* memcpy (c,spell_correct[i],len); */
1100 void capitalize (int line_num, struct eia608_screen *data, int *new_sentence)
1104 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1106 switch (data->characters[line_num][i])
1109 case 0x89: // This is a transparent space
1112 case '.': // Fallthrough
1113 case '?': // Fallthrough
1120 data->characters[line_num][i]=cctoupper (data->characters[line_num][i]);
1122 data->characters[line_num][i]=cctolower (data->characters[line_num][i]);
1129 void find_limit_characters (unsigned char *line, int *first_non_blank, int *last_non_blank)
1134 *first_non_blank=-1;
1135 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1137 unsigned char c=line[i];
1138 if (c!=' ' && c!=0x89)
1140 if (*first_non_blank==-1)
1147 unsigned get_decoder_line_basic (unsigned char *buffer, int line_num, struct eia608_screen *data)
1149 unsigned char *line = data->characters[line_num];
1150 int last_non_blank=-1;
1151 int first_non_blank=-1;
1152 unsigned char *orig=buffer; // Keep for debugging
1154 find_limit_characters (line, &first_non_blank, &last_non_blank);
1156 if (first_non_blank==-1)
1163 for (i=first_non_blank;i<=last_non_blank;i++)
1169 bytes=get_char_in_utf_8 (buffer,c);
1172 get_char_in_latin_1 (buffer,c);
1176 get_char_in_unicode (buffer,c);
1183 return (unsigned) (buffer-orig); // Return length
1186 unsigned get_decoder_line_encoded_for_gui (unsigned char *buffer, int line_num, struct eia608_screen *data)
1188 unsigned char *line = data->characters[line_num];
1189 unsigned char *orig=buffer; // Keep for debugging
1190 int first=0, last=31;
1193 find_limit_characters(line,&first,&last);
1194 for (i=first;i<=last;i++)
1196 get_char_in_latin_1 (buffer,line[i]);
1200 return (unsigned) (buffer-orig); // Return length
1204 unsigned get_decoder_line_encoded (unsigned char *buffer, int line_num, struct eia608_screen *data)
1206 int col = COL_WHITE;
1211 unsigned char *line = data->characters[line_num];
1212 unsigned char *orig=buffer; // Keep for debugging
1213 int first=0, last=31;
1215 find_limit_characters(line,&first,&last);
1216 for (i=first;i<=last;i++)
1219 int its_col = data->colors[line_num][i];
1220 if (its_col != col && !nofontcolor)
1222 if (col!=COL_WHITE) // We need to close the previous font tag
1224 buffer+= encode_line (buffer,(unsigned char *) "</font>");
1227 buffer+=encode_line (buffer, (unsigned char*) color_text[its_col][1]);
1228 if (its_col==COL_USERDEFINED)
1230 // The previous sentence doesn't copy the whole
1231 // <font> tag, just up to the quote before the color
1232 buffer+=encode_line (buffer, (unsigned char*) usercolor_rgb);
1233 buffer+=encode_line (buffer, (unsigned char*) "\">");
1238 // Handle underlined
1239 int is_underlined = data->fonts[line_num][i] & FONT_UNDERLINED;
1240 if (is_underlined && underlined==0) // Open underline
1242 buffer+=encode_line (buffer, (unsigned char *) "<u>");
1244 if (is_underlined==0 && underlined) // Close underline
1246 buffer+=encode_line (buffer, (unsigned char *) "</u>");
1248 underlined=is_underlined;
1250 int has_ita = data->fonts[line_num][i] & FONT_ITALICS;
1251 if (has_ita && italics==0) // Open italics
1253 buffer+=encode_line (buffer, (unsigned char *) "<i>");
1255 if (has_ita==0 && italics) // Close italics
1257 buffer+=encode_line (buffer, (unsigned char *) "</i>");
1264 bytes=get_char_in_utf_8 (buffer,line[i]);
1267 get_char_in_latin_1 (buffer,line[i]);
1271 get_char_in_unicode (buffer,line[i]);
1279 buffer+=encode_line (buffer, (unsigned char *) "</i>");
1283 buffer+=encode_line (buffer, (unsigned char *) "</u>");
1285 if (col != COL_WHITE && !nofontcolor)
1287 buffer+=encode_line (buffer, (unsigned char *) "</font>");
1290 return (unsigned) (buffer-orig); // Return length
1294 void delete_all_lines_but_current (struct eia608_screen *data, int row)
1301 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
1302 data->characters[i][CC608_SCREEN_WIDTH]=0;
1303 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
1304 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
1305 data->row_used[i]=0;
1310 void clear_eia608_cc_buffer (struct eia608_screen *data)
1316 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
1317 data->characters[i][CC608_SCREEN_WIDTH]=0;
1318 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
1319 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
1320 data->row_used[i]=0;
1325 void init_eia608 (struct eia608 *data)
1327 data->cursor_column=0;
1329 clear_eia608_cc_buffer (&data->buffer1);
1330 clear_eia608_cc_buffer (&data->buffer2);
1331 data->visible_buffer=1;
1334 data->mode=MODE_POPUP;
1335 // data->current_visible_start_cc=0;
1336 data->current_visible_start_ms=0;
1337 data->srt_counter=0;
1338 data->screenfuls_counter=0;
1340 data->color=default_color;
1341 data->font=FONT_REGULAR;
1342 data->rollup_base_row=14;
1345 struct eia608_screen *get_writing_buffer (struct s_write *wb)
1347 struct eia608_screen *use_buffer=NULL;
1348 switch (wb->data608->mode)
1350 case MODE_POPUP: // Write on the non-visible buffer
1351 if (wb->data608->visible_buffer==1)
1352 use_buffer = &wb->data608->buffer2;
1354 use_buffer = &wb->data608->buffer1;
1356 case MODE_ROLLUP_2: // Write directly to screen
1359 if (wb->data608->visible_buffer==1)
1360 use_buffer = &wb->data608->buffer1;
1362 use_buffer = &wb->data608->buffer2;
1365 fatal (EXIT_BUG_BUG, "Caption mode has an illegal value at get_writing_buffer(), this is a bug.\n");
1370 void write_char (const unsigned char c, struct s_write *wb)
1372 if (wb->data608->mode!=MODE_TEXT)
1374 struct eia608_screen * use_buffer=get_writing_buffer(wb);
1375 /* hb_log ("\rWriting char [%c] at %s:%d:%d\n",c,
1376 use_buffer == &wb->data608->buffer1?"B1":"B2",
1377 wb->data608->cursor_row,wb->data608->cursor_column); */
1378 use_buffer->characters[wb->data608->cursor_row][wb->data608->cursor_column]=c;
1379 use_buffer->colors[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->color;
1380 use_buffer->fonts[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->font;
1381 use_buffer->row_used[wb->data608->cursor_row]=1;
1382 use_buffer->empty=0;
1383 if (wb->data608->cursor_column<31)
1384 wb->data608->cursor_column++;
1389 /* Handle MID-ROW CODES. */
1390 void handle_text_attr (const unsigned char c1, const unsigned char c2, struct s_write *wb)
1392 // Handle channel change
1393 wb->data608->channel=wb->new_channel;
1394 if (wb->data608->channel!=cc_channel)
1397 hb_log ("\r608: text_attr: %02X %02X",c1,c2);
1398 if ( ((c1!=0x11 && c1!=0x19) ||
1399 (c2<0x20 || c2>0x2f)) && debug_608)
1401 hb_log ("\rThis is not a text attribute!\n");
1406 wb->data608->color=pac2_attribs[i][0];
1407 wb->data608->font=pac2_attribs[i][1];
1409 hb_log (" -- Color: %s, font: %s\n",
1410 color_text[wb->data608->color][0],
1411 font_text[wb->data608->font]);
1412 if (wb->data608->cursor_column<31)
1413 wb->data608->cursor_column++;
1417 void mstotime (LLONG milli, unsigned *hours, unsigned *minutes,
1418 unsigned *seconds, unsigned *ms)
1420 // LLONG milli = (LLONG) ((ccblock*1000)/29.97);
1421 *ms=(unsigned) (milli%1000); // milliseconds
1422 milli=(milli-*ms)/1000; // Remainder, in seconds
1423 *seconds = (int) (milli%60);
1424 milli=(milli-*seconds)/60; // Remainder, in minutes
1425 *minutes = (int) (milli%60);
1426 milli=(milli-*minutes)/60; // Remainder, in hours
1430 void write_subtitle_file_footer (struct s_write *wb)
1432 switch (write_format)
1435 sprintf ((char *) str,"</BODY></SAMI>\n");
1436 if (debug_608 && encoding!=ENC_UNICODE)
1438 hb_log ("\r%s\n", str);
1440 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1441 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1442 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1444 default: // Nothing to do. Only SAMI has a footer
1449 void fhb_log_encoded (FILE *fh, const char *string)
1451 //GUARANTEE(wb, strlen (string)*3);
1452 //wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) string);
1453 //fwrite (wb->enc_buffer,wb->enc_buffer_used,1,fh);
1456 void write_subtitle_file_header (struct s_write *wb)
1458 switch (write_format)
1460 case OF_SRT: // Subrip subtitles have no header
1462 case OF_SAMI: // This header brought to you by McPoodle's CCASDI
1463 //fhb_log_encoded (wb->fh, sami_header);
1464 GUARANTEE(wb, strlen (sami_header)*3);
1465 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) sami_header);
1466 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1467 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1469 case OF_RCWT: // Write header
1470 //fwrite (rcwt_header, sizeof(rcwt_header),1,wb->fh);
1472 case OF_TRANSCRIPT: // No header. Fall thru
1478 void write_cc_line_as_transcript (struct eia608_screen *data, struct s_write *wb, int line_number)
1480 hb_buffer_t *buffer;
1484 capitalize(line_number,data, &wb->new_sentence);
1485 correct_case(line_number,data);
1487 int length = get_decoder_line_basic (wb->subline, line_number, data);
1488 if (debug_608 && encoding!=ENC_UNICODE)
1491 hb_log ("%s\n",wb->subline);
1495 //fwrite (wb->subline, 1, length, wb->fh);
1497 * Put this subtitle in a hb_buffer_t and shove it into the subtitle fifo
1499 buffer = hb_buffer_init( length + 1 );
1500 buffer->start = wb->data608->current_visible_start_ms;
1501 buffer->stop = get_fts(wb);
1502 memcpy( buffer->data, wb->subline, length + 1 );
1503 //hb_log("CC %"PRId64": %s", buffer->stop, wb->subline);
1505 if (wb->hb_last_buffer) {
1506 wb->hb_last_buffer->next = buffer;
1508 wb->hb_buffer = buffer;
1510 wb->hb_last_buffer = buffer;
1512 XMLRPC_APPEND(wb->subline,length);
1513 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1514 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1516 // fhb_log (wb->fh,encoded_crlf);
1519 int write_cc_buffer_as_transcript (struct eia608_screen *data, struct s_write *wb)
1523 int wrote_something = 0;
1526 hb_log ("\n- - - TRANSCRIPT caption - - -\n");
1530 if (data->row_used[i])
1532 write_cc_line_as_transcript (data,wb, i);
1538 hb_log ("- - - - - - - - - - - -\r\n");
1540 return wrote_something;
1543 void write_cc_buffer_to_gui (struct eia608_screen *data, struct s_write *wb)
1545 unsigned h1,m1,s1,ms1;
1546 unsigned h2,m2,s2,ms2;
1549 LLONG ms_start= wb->data608->current_visible_start_ms;
1551 ms_start+=subs_delay;
1552 if (ms_start<0) // Drop screens that because of subs_delay start too early
1554 int time_reported=0;
1557 if (data->row_used[i])
1559 hb_log ("###SUBTITLE#");
1562 LLONG ms_end = get_fts(wb)+subs_delay;
1563 mstotime (ms_start,&h1,&m1,&s1,&ms1);
1564 mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
1565 // Note, only MM:SS here as we need to save space in the preview window
1566 hb_log ("%02u:%02u#%02u:%02u#",
1567 h1*60+m1,s1, h2*60+m2,s2);
1573 // We don't capitalize here because whatever function that was used
1574 // before to write to file already took care of it.
1575 int length = get_decoder_line_encoded_for_gui (wb->subline, i, data);
1576 fwrite (wb->subline, 1, length, stderr);
1577 fwrite ("\n",1,1,stderr);
1583 int write_cc_buffer_as_srt (struct eia608_screen *data, struct s_write *wb)
1585 unsigned h1,m1,s1,ms1;
1586 unsigned h2,m2,s2,ms2;
1587 int wrote_something = 0;
1588 LLONG ms_start= wb->data608->current_visible_start_ms;
1591 ms_start+=subs_delay;
1592 if (ms_start<0) // Drop screens that because of subs_delay start too early
1595 LLONG ms_end = get_fts(wb)+subs_delay;
1596 mstotime (ms_start,&h1,&m1,&s1,&ms1);
1597 mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
1599 wb->data608->srt_counter++;
1600 sprintf (timeline,"%u\r\n",wb->data608->srt_counter);
1601 //wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) timeline);
1602 //fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1603 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1604 //sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n",
1605 // h1,m1,s1,ms1, h2,m2,s2,ms2);
1606 //wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) timeline);
1609 hb_log ("\n- - - SRT caption - - -\n");
1612 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1613 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1616 * Write all the lines into enc_buffer, and then write that out at the end
1617 * ensure that we only have two lines, insert a newline after the first one,
1618 * and have a big bottom line (strip spaces from any joined lines).
1623 if (data->row_used[i])
1627 capitalize(i,data, &wb->new_sentence);
1628 correct_case(i,data);
1631 * The intention was to use a newline but QT doesn't like it, old code still
1632 * here just in case..
1635 wb->enc_buffer_used = get_decoder_line_encoded (wb->enc_buffer, i, data);
1639 wb->enc_buffer_used += encode_line (wb->enc_buffer+wb->enc_buffer_used,
1640 (unsigned char *) "\n");
1643 wb->enc_buffer_used += encode_line (wb->enc_buffer+wb->enc_buffer_used,
1644 (unsigned char *) " ");
1646 wb->enc_buffer_used += get_decoder_line_encoded (wb->enc_buffer+wb->enc_buffer_used, i, data);
1650 if (wb->enc_buffer_used)
1652 hb_buffer_t *buffer = hb_buffer_init( wb->enc_buffer_used + 1 );
1653 buffer->start = ms_start;
1654 buffer->stop = ms_end;
1655 memcpy( buffer->data, wb->enc_buffer, wb->enc_buffer_used + 1 );
1656 if (wb->hb_last_buffer) {
1657 wb->hb_last_buffer->next = buffer;
1659 wb->hb_buffer = buffer;
1661 wb->hb_last_buffer = buffer;
1667 hb_log ("- - - - - - - - - - - -\r\n");
1669 // fhb_log (wb->fh, encoded_crlf);
1670 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1671 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1672 return wrote_something;
1675 int write_cc_buffer_as_sami (struct eia608_screen *data, struct s_write *wb)
1677 int wrote_something=0;
1678 LLONG startms = wb->data608->current_visible_start_ms;
1681 startms+=subs_delay;
1682 if (startms<0) // Drop screens that because of subs_delay start too early
1685 LLONG endms = get_fts(wb)+subs_delay;
1686 endms--; // To prevent overlapping with next line.
1687 sprintf ((char *) str,"<SYNC start=\"%"PRIu64"\"><P class=\"UNKNOWNCC\">\r\n",startms);
1688 if (debug_608 && encoding!=ENC_UNICODE)
1690 hb_log ("\r%s\n", str);
1692 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1693 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1694 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1697 if (data->row_used[i])
1699 int length = get_decoder_line_encoded (wb->subline, i, data);
1700 if (debug_608 && encoding!=ENC_UNICODE)
1703 hb_log ("%s\n",wb->subline);
1705 fwrite (wb->subline, 1, length, wb->fh);
1706 XMLRPC_APPEND(wb->subline,length);
1710 fwrite (encoded_br, 1, encoded_br_length,wb->fh);
1711 XMLRPC_APPEND(encoded_br, encoded_br_length);
1713 fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1714 XMLRPC_APPEND(encoded_crlf, encoded_crlf_length);
1717 sprintf ((char *) str,"</P></SYNC>\r\n");
1718 if (debug_608 && encoding!=ENC_UNICODE)
1720 hb_log ("\r%s\n", str);
1722 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1723 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1724 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1725 sprintf ((char *) str,"<SYNC start=\"%"PRIu64"\"><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",endms);
1726 if (debug_608 && encoding!=ENC_UNICODE)
1728 hb_log ("\r%s\n", str);
1730 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1731 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1732 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1733 return wrote_something;
1736 struct eia608_screen *get_current_visible_buffer (struct s_write *wb)
1738 struct eia608_screen *data;
1739 if (wb->data608->visible_buffer==1)
1740 data = &wb->data608->buffer1;
1742 data = &wb->data608->buffer2;
1747 int write_cc_buffer (struct s_write *wb)
1749 struct eia608_screen *data;
1750 int wrote_something=0;
1751 if (screens_to_process!=-1 && wb->data608->screenfuls_counter>=screens_to_process)
1757 if (wb->data608->visible_buffer==1)
1758 data = &wb->data608->buffer1;
1760 data = &wb->data608->buffer2;
1765 switch (write_format)
1768 wrote_something = write_cc_buffer_as_srt (data, wb);
1771 wrote_something = write_cc_buffer_as_sami (data,wb);
1774 wrote_something = write_cc_buffer_as_transcript (data,wb);
1779 if (wrote_something && gui_mode_reports)
1780 write_cc_buffer_to_gui (data,wb);
1782 return wrote_something;
1785 void roll_up(struct s_write *wb)
1787 struct eia608_screen *use_buffer;
1790 if (wb->data608->visible_buffer==1)
1791 use_buffer = &wb->data608->buffer1;
1793 use_buffer = &wb->data608->buffer2;
1795 switch (wb->data608->mode)
1806 default: // Shouldn't happen
1810 int firstrow=-1, lastrow=-1;
1811 // Look for the last line used
1812 int rows_now=0; // Number of rows in use right now
1815 if (use_buffer->row_used[i])
1825 hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow);
1827 if (lastrow==-1) // Empty screen, nothing to rollup
1830 for (j=lastrow-keep_lines+1;j<lastrow; j++)
1834 memcpy (use_buffer->characters[j],use_buffer->characters[j+1],CC608_SCREEN_WIDTH+1);
1835 memcpy (use_buffer->colors[j],use_buffer->colors[j+1],CC608_SCREEN_WIDTH+1);
1836 memcpy (use_buffer->fonts[j],use_buffer->fonts[j+1],CC608_SCREEN_WIDTH+1);
1837 use_buffer->row_used[j]=use_buffer->row_used[j+1];
1840 for (j=0;j<(1+wb->data608->cursor_row-keep_lines);j++)
1842 memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH);
1843 memset(use_buffer->colors[j],COL_WHITE,CC608_SCREEN_WIDTH);
1844 memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH);
1845 use_buffer->characters[j][CC608_SCREEN_WIDTH]=0;
1846 use_buffer->row_used[j]=0;
1848 memset(use_buffer->characters[lastrow],' ',CC608_SCREEN_WIDTH);
1849 memset(use_buffer->colors[lastrow],COL_WHITE,CC608_SCREEN_WIDTH);
1850 memset(use_buffer->fonts[lastrow],FONT_REGULAR,CC608_SCREEN_WIDTH);
1852 use_buffer->characters[lastrow][CC608_SCREEN_WIDTH]=0;
1853 use_buffer->row_used[lastrow]=0;
1858 if (use_buffer->row_used[i])
1860 if (rows_now>keep_lines)
1861 hb_log ("Bug in roll_up, should have %d lines but I have %d.\n",
1862 keep_lines, rows_now);
1865 void erase_memory (struct s_write *wb, int displayed)
1867 struct eia608_screen *buf;
1870 if (wb->data608->visible_buffer==1)
1871 buf=&wb->data608->buffer1;
1873 buf=&wb->data608->buffer2;
1877 if (wb->data608->visible_buffer==1)
1878 buf=&wb->data608->buffer2;
1880 buf=&wb->data608->buffer1;
1882 clear_eia608_cc_buffer (buf);
1885 int is_current_row_empty (struct s_write *wb)
1887 struct eia608_screen *use_buffer;
1890 if (wb->data608->visible_buffer==1)
1891 use_buffer = &wb->data608->buffer1;
1893 use_buffer = &wb->data608->buffer2;
1894 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1896 if (use_buffer->characters[wb->data608->rollup_base_row][i]!=' ')
1902 /* Process GLOBAL CODES */
1903 void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct s_write *wb)
1905 // Handle channel change
1906 wb->data608->channel=wb->new_channel;
1907 if (wb->data608->channel!=cc_channel)
1910 enum command_code command = COM_UNKNOWN;
1913 if ((c1==0x14 || c1==0x1C) && c2==0x2C)
1914 command = COM_ERASEDISPLAYEDMEMORY;
1915 if ((c1==0x14 || c1==0x1C) && c2==0x20)
1916 command = COM_RESUMECAPTIONLOADING;
1917 if ((c1==0x14 || c1==0x1C) && c2==0x2F)
1918 command = COM_ENDOFCAPTION;
1919 if ((c1==0x17 || c1==0x1F) && c2==0x21)
1920 command = COM_TABOFFSET1;
1921 if ((c1==0x17 || c1==0x1F) && c2==0x22)
1922 command = COM_TABOFFSET2;
1923 if ((c1==0x17 || c1==0x1F) && c2==0x23)
1924 command = COM_TABOFFSET3;
1925 if ((c1==0x14 || c1==0x1C) && c2==0x25)
1926 command = COM_ROLLUP2;
1927 if ((c1==0x14 || c1==0x1C) && c2==0x26)
1928 command = COM_ROLLUP3;
1929 if ((c1==0x14 || c1==0x1C) && c2==0x27)
1930 command = COM_ROLLUP4;
1931 if ((c1==0x14 || c1==0x1C) && c2==0x2D)
1932 command = COM_CARRIAGERETURN;
1933 if ((c1==0x14 || c1==0x1C) && c2==0x2E)
1934 command = COM_ERASENONDISPLAYEDMEMORY;
1935 if ((c1==0x14 || c1==0x1C) && c2==0x21)
1936 command = COM_BACKSPACE;
1937 if ((c1==0x14 || c1==0x1C) && c2==0x2b)
1938 command = COM_RESUMETEXTDISPLAY;
1941 hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]);
1946 if (wb->data608->cursor_column>0)
1948 wb->data608->cursor_column--;
1949 get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' ';
1952 case COM_TABOFFSET1:
1953 if (wb->data608->cursor_column<31)
1954 wb->data608->cursor_column++;
1956 case COM_TABOFFSET2:
1957 wb->data608->cursor_column+=2;
1958 if (wb->data608->cursor_column>31)
1959 wb->data608->cursor_column=31;
1961 case COM_TABOFFSET3:
1962 wb->data608->cursor_column+=3;
1963 if (wb->data608->cursor_column>31)
1964 wb->data608->cursor_column=31;
1966 case COM_RESUMECAPTIONLOADING:
1967 wb->data608->mode=MODE_POPUP;
1969 case COM_RESUMETEXTDISPLAY:
1970 wb->data608->mode=MODE_TEXT;
1973 if (wb->data608->mode==MODE_POPUP)
1975 if (write_cc_buffer (wb))
1976 wb->data608->screenfuls_counter++;
1977 erase_memory (wb, 1);
1979 if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb))
1982 hb_log ("Two RU2, current line not empty. Simulating a CR\n");
1983 handle_command(0x14, 0x2D, wb);
1985 wb->data608->mode=MODE_ROLLUP_2;
1986 erase_memory (wb, 0);
1987 wb->data608->cursor_column=0;
1988 wb->data608->cursor_row=wb->data608->rollup_base_row;
1991 if (wb->data608->mode==MODE_POPUP)
1993 if (write_cc_buffer (wb))
1994 wb->data608->screenfuls_counter++;
1995 erase_memory (wb, 1);
1997 if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb))
2000 hb_log ("Two RU3, current line not empty. Simulating a CR\n");
2001 handle_command(0x14, 0x2D, wb);
2003 wb->data608->mode=MODE_ROLLUP_3;
2004 erase_memory (wb, 0);
2005 wb->data608->cursor_column=0;
2006 wb->data608->cursor_row=wb->data608->rollup_base_row;
2009 if (wb->data608->mode==MODE_POPUP)
2011 if (write_cc_buffer (wb))
2012 wb->data608->screenfuls_counter++;
2013 erase_memory (wb, 1);
2015 if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb))
2018 hb_log ("Two RU4, current line not empty. Simulating a CR\n");
2019 handle_command(0x14, 0x2D, wb);
2022 wb->data608->mode=MODE_ROLLUP_4;
2023 wb->data608->cursor_column=0;
2024 wb->data608->cursor_row=wb->data608->rollup_base_row;
2025 erase_memory (wb, 0);
2027 case COM_CARRIAGERETURN:
2028 // In transcript mode, CR doesn't write the whole screen, to avoid
2030 if (write_format==OF_TRANSCRIPT)
2032 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2037 delete_all_lines_but_current (get_current_visible_buffer (wb), wb->data608->cursor_row);
2038 if (write_cc_buffer(wb))
2039 wb->data608->screenfuls_counter++;
2042 wb->data608->current_visible_start_ms=get_fts(wb);
2043 wb->data608->cursor_column=0;
2045 case COM_ERASENONDISPLAYEDMEMORY:
2046 erase_memory (wb,0);
2048 case COM_ERASEDISPLAYEDMEMORY:
2049 // Write it to disk before doing this, and make a note of the new
2050 // time it became clear.
2051 if (write_format==OF_TRANSCRIPT &&
2052 (wb->data608->mode==MODE_ROLLUP_2 || wb->data608->mode==MODE_ROLLUP_3 ||
2053 wb->data608->mode==MODE_ROLLUP_4))
2055 // In transcript mode we just write the cursor line. The previous lines
2056 // should have been written already, so writing everything produces
2058 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2062 if (write_cc_buffer (wb))
2063 wb->data608->screenfuls_counter++;
2065 erase_memory (wb,1);
2066 wb->data608->current_visible_start_ms=get_fts(wb);
2068 case COM_ENDOFCAPTION: // Switch buffers
2069 // The currently *visible* buffer is leaving, so now we know it's ending
2070 // time. Time to actually write it to file.
2071 if (write_cc_buffer (wb))
2072 wb->data608->screenfuls_counter++;
2073 wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1;
2074 wb->data608->current_visible_start_ms=get_fts(wb);
2075 wb->data608->cursor_column=0;
2076 wb->data608->cursor_row=0;
2077 wb->data608->color=default_color;
2078 wb->data608->font=FONT_REGULAR;
2083 hb_log ("\rNot yet implemented.\n");
2089 void handle_end_of_data (struct s_write *wb)
2091 // We issue a EraseDisplayedMemory here so if there's any captions pending
2092 // they get written to file.
2093 handle_command (0x14, 0x2c, wb); // EDM
2096 void handle_double (const unsigned char c1, const unsigned char c2, struct s_write *wb)
2099 if (wb->data608->channel!=cc_channel)
2101 if (c2>=0x30 && c2<=0x3f)
2103 c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here
2105 hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c);
2110 /* Process EXTENDED CHARACTERS */
2111 unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_write *wb)
2113 // Handle channel change
2114 if (wb->new_channel > 2)
2116 wb->new_channel -= 2;
2118 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2120 wb->data608->channel=wb->new_channel;
2121 if (wb->data608->channel!=cc_channel)
2124 // For lo values between 0x20-0x3f
2128 hb_log ("\rExtended: %02X %02X\n",hi,lo);
2129 if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13))
2134 c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here
2137 c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here
2140 // This column change is because extended characters replace
2141 // the previous character (which is sent for basic decoders
2142 // to show something similar to the real char)
2143 if (wb->data608->cursor_column>0)
2144 wb->data608->cursor_column--;
2151 /* Process PREAMBLE ACCESS CODES (PAC) */
2152 void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb)
2154 // Handle channel change
2155 if (wb->new_channel > 2)
2157 wb->new_channel -= 2;
2159 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2161 wb->data608->channel=wb->new_channel;
2162 if (wb->data608->channel!=cc_channel)
2165 int row=rowdata[((c1<<1)&14)|((c2>>5)&1)];
2168 hb_log ("\rPAC: %02X %02X",c1,c2);
2170 if (c2>=0x40 && c2<=0x5f)
2176 if (c2>=0x60 && c2<=0x7f)
2183 hb_log ("\rThis is not a PAC!!!!!\n");
2187 int color=pac2_attribs[c2][0];
2188 int font=pac2_attribs[c2][1];
2189 int indent=pac2_attribs[c2][2];
2191 hb_log (" -- Position: %d:%d, color: %s, font: %s\n",row,
2192 indent,color_text[color][0],font_text[font]);
2193 if (wb->data608->mode!=MODE_TEXT)
2195 // According to Robson, row info is discarded in text mode
2196 // but column is accepted
2197 wb->data608->cursor_row=row-1 ; // Since the array is 0 based
2199 wb->data608->rollup_base_row=row-1;
2200 wb->data608->cursor_column=indent;
2204 void handle_single (const unsigned char c1, struct s_write *wb)
2206 if (c1<0x20 || wb->data608->channel!=cc_channel)
2207 return; // We don't allow special stuff here
2212 hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */
2216 int check_channel (unsigned char c1, struct s_write *wb)
2220 if (debug_608 && wb->data608->channel!=1)
2221 hb_log ("\nChannel change, now 1\n");
2226 if (debug_608 && wb->data608->channel!=2)
2227 hb_log ("\nChannel change, now 2\n");
2232 if (debug_608 && wb->data608->channel!=3)
2233 hb_log ("\nChannel change, now 3\n");
2238 if (debug_608 && wb->data608->channel!=4)
2239 hb_log ("\nChannel change, now 4\n");
2243 // Otherwise keep the current channel
2244 return wb->data608->channel;
2247 /* Handle Command, special char or attribute and also check for
2249 * Returns 1 if something was written to screen, 0 otherwise */
2250 int disCommand (unsigned char hi, unsigned char lo, struct s_write *wb)
2252 int wrote_to_screen=0;
2254 /* Full channel changes are only allowed for "GLOBAL CODES",
2255 * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES",
2257 * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and
2258 * SPECIAL/SPECIAL CHARACTERS allow only switching
2259 * between 1&3 or 2&4. */
2260 wb->new_channel = check_channel (hi,wb);
2261 //if (wb->data608->channel!=cc_channel)
2264 if (hi>=0x18 && hi<=0x1f)
2270 if (lo>=0x40 && lo<=0x5f)
2271 handle_pac (hi,lo,wb);
2274 if (lo>=0x20 && lo<=0x2f)
2275 handle_text_attr (hi,lo,wb);
2276 if (lo>=0x30 && lo<=0x3f)
2279 handle_double (hi,lo,wb);
2281 if (lo>=0x40 && lo<=0x7f)
2282 handle_pac (hi,lo,wb);
2286 if (lo>=0x20 && lo<=0x3f)
2288 wrote_to_screen=handle_extended (hi,lo,wb);
2290 if (lo>=0x40 && lo<=0x7f)
2291 handle_pac (hi,lo,wb);
2295 if (lo>=0x20 && lo<=0x2f)
2296 handle_command (hi,lo,wb);
2297 if (lo>=0x40 && lo<=0x7f)
2298 handle_pac (hi,lo,wb);
2301 if (lo>=0x40 && lo<=0x7f)
2302 handle_pac (hi,lo,wb);
2305 if (lo>=0x21 && lo<=0x22)
2306 handle_command (hi,lo,wb);
2307 if (lo>=0x2e && lo<=0x2f)
2308 handle_text_attr (hi,lo,wb);
2309 if (lo>=0x40 && lo<=0x7f)
2310 handle_pac (hi,lo,wb);
2313 return wrote_to_screen;
2316 void process608 (const unsigned char *data, int length, struct s_write *wb)
2318 static int textprinted = 0;
2323 for (i=0;i<length;i=i+2)
2325 unsigned char hi, lo;
2326 int wrote_to_screen=0;
2327 hi = data[i] & 0x7F; // Get rid of parity bit
2328 lo = data[i+1] & 0x7F; // Get rid of parity bit
2330 if (hi==0 && lo==0) // Just padding
2332 // hb_log ("\r[%02X:%02X]\n",hi,lo);
2334 if (hi>=0x01 && hi<=0x0E)
2336 // XDS crap - mode. Would be nice to support it eventually
2337 // wb->data608->last_c1=0;
2338 // wb->data608->last_c2=0;
2339 wb->data608->channel=3; // Always channel 3
2342 if (hi==0x0F) // End of XDS block
2347 if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char
2348 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
2349 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
2351 // We were writing characters before, start a new line for
2352 // diagnostic output from disCommand()
2353 if (debug_608 && textprinted == 1 )
2359 wb->in_xds_mode=0; // Back to normal
2360 if (wb->data608->last_c1==hi && wb->data608->last_c2==lo)
2362 // Duplicate dual code, discard
2365 wb->data608->last_c1=hi;
2366 wb->data608->last_c2=lo;
2367 wrote_to_screen=disCommand (hi,lo,wb);
2369 if (hi>=0x20) // Standard characters (always in pairs)
2371 // Only print if the channel is active
2372 if (wb->data608->channel!=cc_channel)
2377 if( textprinted == 0 )
2384 handle_single(hi,wb);
2385 handle_single(lo,wb);
2387 wb->data608->last_c1=0;
2388 wb->data608->last_c2=0;
2391 if ( debug_608 && !textprinted && wb->data608->channel==cc_channel )
2392 { // Current FTS information after the characters are shown
2393 //hb_log("Current FTS: %s\n", print_mstime(get_fts()));
2396 if (wrote_to_screen && direct_rollup && // If direct_rollup is enabled and
2397 (wb->data608->mode==MODE_ROLLUP_2 || // we are in rollup mode, write now.
2398 wb->data608->mode==MODE_ROLLUP_3 ||
2399 wb->data608->mode==MODE_ROLLUP_4))
2401 // We don't increase screenfuls_counter here.
2402 write_cc_buffer (wb);
2403 wb->data608->current_visible_start_ms=get_fts(wb);
2410 /* Return a pointer to a string that holds the printable characters
2411 * of the caption data block. FOR DEBUG PURPOSES ONLY! */
2412 unsigned char *debug_608toASC (unsigned char *cc_data, int channel)
2414 static unsigned char output[3];
2416 unsigned char cc_valid = (cc_data[0] & 4) >>2;
2417 unsigned char cc_type = cc_data[0] & 3;
2418 unsigned char hi, lo;
2424 if (cc_valid && cc_type==channel)
2426 hi = cc_data[1] & 0x7F; // Get rid of parity bit
2427 lo = cc_data[2] & 0x7F; // Get rid of parity bit
2431 output[1]=(lo>=20 ? lo : '.');
2445 struct hb_work_private_s
2448 struct s_write * cc608;
2451 int decccInit( hb_work_object_t * w, hb_job_t * job )
2454 hb_work_private_t * pv;
2456 pv = calloc( 1, sizeof( hb_work_private_t ) );
2459 w->private_data = pv;
2463 pv->cc608 = calloc(1, sizeof(struct s_write));
2467 retval = general_608_init(pv->cc608);
2470 pv->cc608->data608 = calloc(1, sizeof(struct eia608));
2471 if( !pv->cc608->data608 )
2481 int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
2482 hb_buffer_t ** buf_out )
2484 hb_work_private_t * pv = w->private_data;
2485 hb_buffer_t * in = *buf_in;
2487 if ( in->size <= 0 )
2489 /* EOF on input stream - send it downstream & say that we're done */
2490 handle_end_of_data(pv->cc608);
2492 * Grab any pending buffer and output them with the EOF on the end
2494 if (pv->cc608->hb_last_buffer) {
2495 pv->cc608->hb_last_buffer->next = in;
2496 *buf_out = pv->cc608->hb_buffer;
2498 pv->cc608->hb_buffer = NULL;
2499 pv->cc608->hb_last_buffer = NULL;
2504 return HB_WORK_DONE;
2507 pv->cc608->last_pts = in->start;
2509 process608(in->data, in->size, pv->cc608);
2512 * If there is one waiting then pass it on
2514 *buf_out = pv->cc608->hb_buffer;
2515 pv->cc608->hb_buffer = NULL;
2516 pv->cc608->hb_last_buffer = NULL;
2521 void decccClose( hb_work_object_t * w )
2523 hb_work_private_t * pv = w->private_data;
2524 general_608_close( pv->cc608 );
2525 free( pv->cc608->data608 );
2527 free( w->private_data );
2530 hb_work_object_t hb_deccc608 =
2533 "Closed Caption (608) decoder",