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 %lld: %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 *) " ");
1642 wb->enc_buffer_used += encode_line (wb->enc_buffer+wb->enc_buffer_used,
1643 (unsigned char *) " ");
1645 wb->enc_buffer_used += get_decoder_line_encoded (wb->enc_buffer+wb->enc_buffer_used, i, data);
1649 if (wb->enc_buffer_used)
1651 hb_buffer_t *buffer = hb_buffer_init( wb->enc_buffer_used + 1 );
1652 buffer->start = ms_start;
1653 buffer->stop = ms_end;
1654 memcpy( buffer->data, wb->enc_buffer, wb->enc_buffer_used + 1 );
1655 if (wb->hb_last_buffer) {
1656 wb->hb_last_buffer->next = buffer;
1658 wb->hb_buffer = buffer;
1660 wb->hb_last_buffer = buffer;
1666 hb_log ("- - - - - - - - - - - -\r\n");
1668 // fhb_log (wb->fh, encoded_crlf);
1669 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1670 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1671 return wrote_something;
1674 int write_cc_buffer_as_sami (struct eia608_screen *data, struct s_write *wb)
1676 int wrote_something=0;
1677 LLONG startms = wb->data608->current_visible_start_ms;
1680 startms+=subs_delay;
1681 if (startms<0) // Drop screens that because of subs_delay start too early
1684 LLONG endms = get_fts(wb)+subs_delay;
1685 endms--; // To prevent overlapping with next line.
1686 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\">\r\n",startms);
1687 if (debug_608 && encoding!=ENC_UNICODE)
1689 hb_log ("\r%s\n", str);
1691 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1692 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1693 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1696 if (data->row_used[i])
1698 int length = get_decoder_line_encoded (wb->subline, i, data);
1699 if (debug_608 && encoding!=ENC_UNICODE)
1702 hb_log ("%s\n",wb->subline);
1704 fwrite (wb->subline, 1, length, wb->fh);
1705 XMLRPC_APPEND(wb->subline,length);
1709 fwrite (encoded_br, 1, encoded_br_length,wb->fh);
1710 XMLRPC_APPEND(encoded_br, encoded_br_length);
1712 fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1713 XMLRPC_APPEND(encoded_crlf, encoded_crlf_length);
1716 sprintf ((char *) str,"</P></SYNC>\r\n");
1717 if (debug_608 && encoding!=ENC_UNICODE)
1719 hb_log ("\r%s\n", str);
1721 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1722 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1723 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1724 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",endms);
1725 if (debug_608 && encoding!=ENC_UNICODE)
1727 hb_log ("\r%s\n", str);
1729 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1730 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1731 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1732 return wrote_something;
1735 struct eia608_screen *get_current_visible_buffer (struct s_write *wb)
1737 struct eia608_screen *data;
1738 if (wb->data608->visible_buffer==1)
1739 data = &wb->data608->buffer1;
1741 data = &wb->data608->buffer2;
1746 int write_cc_buffer (struct s_write *wb)
1748 struct eia608_screen *data;
1749 int wrote_something=0;
1750 if (screens_to_process!=-1 && wb->data608->screenfuls_counter>=screens_to_process)
1756 if (wb->data608->visible_buffer==1)
1757 data = &wb->data608->buffer1;
1759 data = &wb->data608->buffer2;
1764 switch (write_format)
1767 wrote_something = write_cc_buffer_as_srt (data, wb);
1770 wrote_something = write_cc_buffer_as_sami (data,wb);
1773 wrote_something = write_cc_buffer_as_transcript (data,wb);
1778 if (wrote_something && gui_mode_reports)
1779 write_cc_buffer_to_gui (data,wb);
1781 return wrote_something;
1784 void roll_up(struct s_write *wb)
1786 struct eia608_screen *use_buffer;
1789 if (wb->data608->visible_buffer==1)
1790 use_buffer = &wb->data608->buffer1;
1792 use_buffer = &wb->data608->buffer2;
1794 switch (wb->data608->mode)
1805 default: // Shouldn't happen
1809 int firstrow=-1, lastrow=-1;
1810 // Look for the last line used
1811 int rows_now=0; // Number of rows in use right now
1814 if (use_buffer->row_used[i])
1824 hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow);
1826 if (lastrow==-1) // Empty screen, nothing to rollup
1829 for (j=lastrow-keep_lines+1;j<lastrow; j++)
1833 memcpy (use_buffer->characters[j],use_buffer->characters[j+1],CC608_SCREEN_WIDTH+1);
1834 memcpy (use_buffer->colors[j],use_buffer->colors[j+1],CC608_SCREEN_WIDTH+1);
1835 memcpy (use_buffer->fonts[j],use_buffer->fonts[j+1],CC608_SCREEN_WIDTH+1);
1836 use_buffer->row_used[j]=use_buffer->row_used[j+1];
1839 for (j=0;j<(1+wb->data608->cursor_row-keep_lines);j++)
1841 memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH);
1842 memset(use_buffer->colors[j],COL_WHITE,CC608_SCREEN_WIDTH);
1843 memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH);
1844 use_buffer->characters[j][CC608_SCREEN_WIDTH]=0;
1845 use_buffer->row_used[j]=0;
1847 memset(use_buffer->characters[lastrow],' ',CC608_SCREEN_WIDTH);
1848 memset(use_buffer->colors[lastrow],COL_WHITE,CC608_SCREEN_WIDTH);
1849 memset(use_buffer->fonts[lastrow],FONT_REGULAR,CC608_SCREEN_WIDTH);
1851 use_buffer->characters[lastrow][CC608_SCREEN_WIDTH]=0;
1852 use_buffer->row_used[lastrow]=0;
1857 if (use_buffer->row_used[i])
1859 if (rows_now>keep_lines)
1860 hb_log ("Bug in roll_up, should have %d lines but I have %d.\n",
1861 keep_lines, rows_now);
1864 void erase_memory (struct s_write *wb, int displayed)
1866 struct eia608_screen *buf;
1869 if (wb->data608->visible_buffer==1)
1870 buf=&wb->data608->buffer1;
1872 buf=&wb->data608->buffer2;
1876 if (wb->data608->visible_buffer==1)
1877 buf=&wb->data608->buffer2;
1879 buf=&wb->data608->buffer1;
1881 clear_eia608_cc_buffer (buf);
1884 int is_current_row_empty (struct s_write *wb)
1886 struct eia608_screen *use_buffer;
1889 if (wb->data608->visible_buffer==1)
1890 use_buffer = &wb->data608->buffer1;
1892 use_buffer = &wb->data608->buffer2;
1893 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1895 if (use_buffer->characters[wb->data608->rollup_base_row][i]!=' ')
1901 /* Process GLOBAL CODES */
1902 void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct s_write *wb)
1904 // Handle channel change
1905 wb->data608->channel=wb->new_channel;
1906 if (wb->data608->channel!=cc_channel)
1909 enum command_code command = COM_UNKNOWN;
1912 if ((c1==0x14 || c1==0x1C) && c2==0x2C)
1913 command = COM_ERASEDISPLAYEDMEMORY;
1914 if ((c1==0x14 || c1==0x1C) && c2==0x20)
1915 command = COM_RESUMECAPTIONLOADING;
1916 if ((c1==0x14 || c1==0x1C) && c2==0x2F)
1917 command = COM_ENDOFCAPTION;
1918 if ((c1==0x17 || c1==0x1F) && c2==0x21)
1919 command = COM_TABOFFSET1;
1920 if ((c1==0x17 || c1==0x1F) && c2==0x22)
1921 command = COM_TABOFFSET2;
1922 if ((c1==0x17 || c1==0x1F) && c2==0x23)
1923 command = COM_TABOFFSET3;
1924 if ((c1==0x14 || c1==0x1C) && c2==0x25)
1925 command = COM_ROLLUP2;
1926 if ((c1==0x14 || c1==0x1C) && c2==0x26)
1927 command = COM_ROLLUP3;
1928 if ((c1==0x14 || c1==0x1C) && c2==0x27)
1929 command = COM_ROLLUP4;
1930 if ((c1==0x14 || c1==0x1C) && c2==0x2D)
1931 command = COM_CARRIAGERETURN;
1932 if ((c1==0x14 || c1==0x1C) && c2==0x2E)
1933 command = COM_ERASENONDISPLAYEDMEMORY;
1934 if ((c1==0x14 || c1==0x1C) && c2==0x21)
1935 command = COM_BACKSPACE;
1936 if ((c1==0x14 || c1==0x1C) && c2==0x2b)
1937 command = COM_RESUMETEXTDISPLAY;
1940 hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]);
1945 if (wb->data608->cursor_column>0)
1947 wb->data608->cursor_column--;
1948 get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' ';
1951 case COM_TABOFFSET1:
1952 if (wb->data608->cursor_column<31)
1953 wb->data608->cursor_column++;
1955 case COM_TABOFFSET2:
1956 wb->data608->cursor_column+=2;
1957 if (wb->data608->cursor_column>31)
1958 wb->data608->cursor_column=31;
1960 case COM_TABOFFSET3:
1961 wb->data608->cursor_column+=3;
1962 if (wb->data608->cursor_column>31)
1963 wb->data608->cursor_column=31;
1965 case COM_RESUMECAPTIONLOADING:
1966 wb->data608->mode=MODE_POPUP;
1968 case COM_RESUMETEXTDISPLAY:
1969 wb->data608->mode=MODE_TEXT;
1972 if (wb->data608->mode==MODE_POPUP)
1974 if (write_cc_buffer (wb))
1975 wb->data608->screenfuls_counter++;
1976 erase_memory (wb, 1);
1978 if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb))
1981 hb_log ("Two RU2, current line not empty. Simulating a CR\n");
1982 handle_command(0x14, 0x2D, wb);
1984 wb->data608->mode=MODE_ROLLUP_2;
1985 erase_memory (wb, 0);
1986 wb->data608->cursor_column=0;
1987 wb->data608->cursor_row=wb->data608->rollup_base_row;
1990 if (wb->data608->mode==MODE_POPUP)
1992 if (write_cc_buffer (wb))
1993 wb->data608->screenfuls_counter++;
1994 erase_memory (wb, 1);
1996 if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb))
1999 hb_log ("Two RU3, current line not empty. Simulating a CR\n");
2000 handle_command(0x14, 0x2D, wb);
2002 wb->data608->mode=MODE_ROLLUP_3;
2003 erase_memory (wb, 0);
2004 wb->data608->cursor_column=0;
2005 wb->data608->cursor_row=wb->data608->rollup_base_row;
2008 if (wb->data608->mode==MODE_POPUP)
2010 if (write_cc_buffer (wb))
2011 wb->data608->screenfuls_counter++;
2012 erase_memory (wb, 1);
2014 if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb))
2017 hb_log ("Two RU4, current line not empty. Simulating a CR\n");
2018 handle_command(0x14, 0x2D, wb);
2021 wb->data608->mode=MODE_ROLLUP_4;
2022 wb->data608->cursor_column=0;
2023 wb->data608->cursor_row=wb->data608->rollup_base_row;
2024 erase_memory (wb, 0);
2026 case COM_CARRIAGERETURN:
2027 // In transcript mode, CR doesn't write the whole screen, to avoid
2029 if (write_format==OF_TRANSCRIPT)
2031 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2036 delete_all_lines_but_current (get_current_visible_buffer (wb), wb->data608->cursor_row);
2037 if (write_cc_buffer(wb))
2038 wb->data608->screenfuls_counter++;
2041 wb->data608->current_visible_start_ms=get_fts(wb);
2042 wb->data608->cursor_column=0;
2044 case COM_ERASENONDISPLAYEDMEMORY:
2045 erase_memory (wb,0);
2047 case COM_ERASEDISPLAYEDMEMORY:
2048 // Write it to disk before doing this, and make a note of the new
2049 // time it became clear.
2050 if (write_format==OF_TRANSCRIPT &&
2051 (wb->data608->mode==MODE_ROLLUP_2 || wb->data608->mode==MODE_ROLLUP_3 ||
2052 wb->data608->mode==MODE_ROLLUP_4))
2054 // In transcript mode we just write the cursor line. The previous lines
2055 // should have been written already, so writing everything produces
2057 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2061 if (write_cc_buffer (wb))
2062 wb->data608->screenfuls_counter++;
2064 erase_memory (wb,1);
2065 wb->data608->current_visible_start_ms=get_fts(wb);
2067 case COM_ENDOFCAPTION: // Switch buffers
2068 // The currently *visible* buffer is leaving, so now we know it's ending
2069 // time. Time to actually write it to file.
2070 if (write_cc_buffer (wb))
2071 wb->data608->screenfuls_counter++;
2072 wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1;
2073 wb->data608->current_visible_start_ms=get_fts(wb);
2074 wb->data608->cursor_column=0;
2075 wb->data608->cursor_row=0;
2076 wb->data608->color=default_color;
2077 wb->data608->font=FONT_REGULAR;
2082 hb_log ("\rNot yet implemented.\n");
2088 void handle_end_of_data (struct s_write *wb)
2090 // We issue a EraseDisplayedMemory here so if there's any captions pending
2091 // they get written to file.
2092 handle_command (0x14, 0x2c, wb); // EDM
2095 void handle_double (const unsigned char c1, const unsigned char c2, struct s_write *wb)
2098 if (wb->data608->channel!=cc_channel)
2100 if (c2>=0x30 && c2<=0x3f)
2102 c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here
2104 hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c);
2109 /* Process EXTENDED CHARACTERS */
2110 unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_write *wb)
2112 // Handle channel change
2113 if (wb->new_channel > 2)
2115 wb->new_channel -= 2;
2117 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2119 wb->data608->channel=wb->new_channel;
2120 if (wb->data608->channel!=cc_channel)
2123 // For lo values between 0x20-0x3f
2127 hb_log ("\rExtended: %02X %02X\n",hi,lo);
2128 if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13))
2133 c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here
2136 c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here
2139 // This column change is because extended characters replace
2140 // the previous character (which is sent for basic decoders
2141 // to show something similar to the real char)
2142 if (wb->data608->cursor_column>0)
2143 wb->data608->cursor_column--;
2150 /* Process PREAMBLE ACCESS CODES (PAC) */
2151 void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb)
2153 // Handle channel change
2154 if (wb->new_channel > 2)
2156 wb->new_channel -= 2;
2158 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2160 wb->data608->channel=wb->new_channel;
2161 if (wb->data608->channel!=cc_channel)
2164 int row=rowdata[((c1<<1)&14)|((c2>>5)&1)];
2167 hb_log ("\rPAC: %02X %02X",c1,c2);
2169 if (c2>=0x40 && c2<=0x5f)
2175 if (c2>=0x60 && c2<=0x7f)
2182 hb_log ("\rThis is not a PAC!!!!!\n");
2186 int color=pac2_attribs[c2][0];
2187 int font=pac2_attribs[c2][1];
2188 int indent=pac2_attribs[c2][2];
2190 hb_log (" -- Position: %d:%d, color: %s, font: %s\n",row,
2191 indent,color_text[color][0],font_text[font]);
2192 if (wb->data608->mode!=MODE_TEXT)
2194 // According to Robson, row info is discarded in text mode
2195 // but column is accepted
2196 wb->data608->cursor_row=row-1 ; // Since the array is 0 based
2198 wb->data608->rollup_base_row=row-1;
2199 wb->data608->cursor_column=indent;
2203 void handle_single (const unsigned char c1, struct s_write *wb)
2205 if (c1<0x20 || wb->data608->channel!=cc_channel)
2206 return; // We don't allow special stuff here
2211 hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */
2215 int check_channel (unsigned char c1, struct s_write *wb)
2219 if (debug_608 && wb->data608->channel!=1)
2220 hb_log ("\nChannel change, now 1\n");
2225 if (debug_608 && wb->data608->channel!=2)
2226 hb_log ("\nChannel change, now 2\n");
2231 if (debug_608 && wb->data608->channel!=3)
2232 hb_log ("\nChannel change, now 3\n");
2237 if (debug_608 && wb->data608->channel!=4)
2238 hb_log ("\nChannel change, now 4\n");
2242 // Otherwise keep the current channel
2243 return wb->data608->channel;
2246 /* Handle Command, special char or attribute and also check for
2248 * Returns 1 if something was written to screen, 0 otherwise */
2249 int disCommand (unsigned char hi, unsigned char lo, struct s_write *wb)
2251 int wrote_to_screen=0;
2253 /* Full channel changes are only allowed for "GLOBAL CODES",
2254 * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES",
2256 * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and
2257 * SPECIAL/SPECIAL CHARACTERS allow only switching
2258 * between 1&3 or 2&4. */
2259 wb->new_channel = check_channel (hi,wb);
2260 //if (wb->data608->channel!=cc_channel)
2263 if (hi>=0x18 && hi<=0x1f)
2269 if (lo>=0x40 && lo<=0x5f)
2270 handle_pac (hi,lo,wb);
2273 if (lo>=0x20 && lo<=0x2f)
2274 handle_text_attr (hi,lo,wb);
2275 if (lo>=0x30 && lo<=0x3f)
2278 handle_double (hi,lo,wb);
2280 if (lo>=0x40 && lo<=0x7f)
2281 handle_pac (hi,lo,wb);
2285 if (lo>=0x20 && lo<=0x3f)
2287 wrote_to_screen=handle_extended (hi,lo,wb);
2289 if (lo>=0x40 && lo<=0x7f)
2290 handle_pac (hi,lo,wb);
2294 if (lo>=0x20 && lo<=0x2f)
2295 handle_command (hi,lo,wb);
2296 if (lo>=0x40 && lo<=0x7f)
2297 handle_pac (hi,lo,wb);
2300 if (lo>=0x40 && lo<=0x7f)
2301 handle_pac (hi,lo,wb);
2304 if (lo>=0x21 && lo<=0x22)
2305 handle_command (hi,lo,wb);
2306 if (lo>=0x2e && lo<=0x2f)
2307 handle_text_attr (hi,lo,wb);
2308 if (lo>=0x40 && lo<=0x7f)
2309 handle_pac (hi,lo,wb);
2312 return wrote_to_screen;
2315 void process608 (const unsigned char *data, int length, struct s_write *wb)
2317 static int textprinted = 0;
2322 for (i=0;i<length;i=i+2)
2324 unsigned char hi, lo;
2325 int wrote_to_screen=0;
2326 hi = data[i] & 0x7F; // Get rid of parity bit
2327 lo = data[i+1] & 0x7F; // Get rid of parity bit
2329 if (hi==0 && lo==0) // Just padding
2331 // hb_log ("\r[%02X:%02X]\n",hi,lo);
2333 if (hi>=0x01 && hi<=0x0E)
2335 // XDS crap - mode. Would be nice to support it eventually
2336 // wb->data608->last_c1=0;
2337 // wb->data608->last_c2=0;
2338 wb->data608->channel=3; // Always channel 3
2341 if (hi==0x0F) // End of XDS block
2346 if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char
2347 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
2348 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
2350 // We were writing characters before, start a new line for
2351 // diagnostic output from disCommand()
2352 if (debug_608 && textprinted == 1 )
2358 wb->in_xds_mode=0; // Back to normal
2359 if (wb->data608->last_c1==hi && wb->data608->last_c2==lo)
2361 // Duplicate dual code, discard
2364 wb->data608->last_c1=hi;
2365 wb->data608->last_c2=lo;
2366 wrote_to_screen=disCommand (hi,lo,wb);
2368 if (hi>=0x20) // Standard characters (always in pairs)
2370 // Only print if the channel is active
2371 if (wb->data608->channel!=cc_channel)
2376 if( textprinted == 0 )
2383 handle_single(hi,wb);
2384 handle_single(lo,wb);
2386 wb->data608->last_c1=0;
2387 wb->data608->last_c2=0;
2390 if ( debug_608 && !textprinted && wb->data608->channel==cc_channel )
2391 { // Current FTS information after the characters are shown
2392 //hb_log("Current FTS: %s\n", print_mstime(get_fts()));
2395 if (wrote_to_screen && direct_rollup && // If direct_rollup is enabled and
2396 (wb->data608->mode==MODE_ROLLUP_2 || // we are in rollup mode, write now.
2397 wb->data608->mode==MODE_ROLLUP_3 ||
2398 wb->data608->mode==MODE_ROLLUP_4))
2400 // We don't increase screenfuls_counter here.
2401 write_cc_buffer (wb);
2402 wb->data608->current_visible_start_ms=get_fts(wb);
2409 /* Return a pointer to a string that holds the printable characters
2410 * of the caption data block. FOR DEBUG PURPOSES ONLY! */
2411 unsigned char *debug_608toASC (unsigned char *cc_data, int channel)
2413 static unsigned char output[3];
2415 unsigned char cc_valid = (cc_data[0] & 4) >>2;
2416 unsigned char cc_type = cc_data[0] & 3;
2417 unsigned char hi, lo;
2423 if (cc_valid && cc_type==channel)
2425 hi = cc_data[1] & 0x7F; // Get rid of parity bit
2426 lo = cc_data[2] & 0x7F; // Get rid of parity bit
2430 output[1]=(lo>=20 ? lo : '.');
2444 struct hb_work_private_s
2447 struct s_write * cc608;
2450 int decccInit( hb_work_object_t * w, hb_job_t * job )
2453 hb_work_private_t * pv;
2455 pv = calloc( 1, sizeof( hb_work_private_t ) );
2458 w->private_data = pv;
2462 pv->cc608 = calloc(1, sizeof(struct s_write));
2466 retval = general_608_init(pv->cc608);
2469 pv->cc608->data608 = calloc(1, sizeof(struct eia608));
2470 if( !pv->cc608->data608 )
2480 int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
2481 hb_buffer_t ** buf_out )
2483 hb_work_private_t * pv = w->private_data;
2484 hb_buffer_t * in = *buf_in;
2486 if ( in->size <= 0 )
2488 /* EOF on input stream - send it downstream & say that we're done */
2489 handle_end_of_data(pv->cc608);
2491 * Grab any pending buffer and output them with the EOF on the end
2493 if (pv->cc608->hb_last_buffer) {
2494 pv->cc608->hb_last_buffer->next = in;
2495 *buf_out = pv->cc608->hb_buffer;
2497 pv->cc608->hb_buffer = NULL;
2498 pv->cc608->hb_last_buffer = NULL;
2503 return HB_WORK_DONE;
2506 pv->cc608->last_pts = in->start;
2508 process608(in->data, in->size, pv->cc608);
2511 * If there is one waiting then pass it on
2513 *buf_out = pv->cc608->hb_buffer;
2514 pv->cc608->hb_buffer = NULL;
2515 pv->cc608->hb_last_buffer = NULL;
2520 void decccClose( hb_work_object_t * w )
2522 hb_work_private_t * pv = w->private_data;
2523 general_608_close( pv->cc608 );
2524 free( pv->cc608->data608 );
2526 free( w->private_data );
2529 hb_work_object_t hb_deccc608 =
2532 "Closed Caption (608) decoder",