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 = 1;
20 static int subs_delay = 0;
21 static LLONG screens_to_process = -1;
22 static int processed_enough = 0;
23 static int gui_mode_reports = 0;
24 static int norollup = 1;
25 static int direct_rollup = 0;
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
538 if (c<0x80) // Regular line-21 character set, mostly ASCII except these exceptions
542 case 0x2a: // lowercase a, acute accent
546 case 0x5c: // lowercase e, acute accent
550 case 0x5e: // lowercase i, acute accent
554 case 0x5f: // lowercase o, acute accent
558 case 0x60: // lowercase u, acute accent
562 case 0x7b: // lowercase c with cedilla
566 case 0x7c: // division symbol
570 case 0x7d: // uppercase N tilde
574 case 0x7e: // lowercase n tilde
585 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
586 // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
587 case 0x80: // Registered symbol (R)
591 case 0x81: // degree sign
595 case 0x82: // 1/2 symbol
599 case 0x83: // Inverted (open) question mark
603 case 0x84: // Trademark symbol (TM)
608 case 0x85: // Cents symbol
612 case 0x86: // Pounds sterling
616 case 0x87: // Music note
621 case 0x88: // lowercase a, grave accent
625 case 0x89: // transparent space, we make it regular
628 case 0x8a: // lowercase e, grave accent
632 case 0x8b: // lowercase a, circumflex accent
636 case 0x8c: // lowercase e, circumflex accent
640 case 0x8d: // lowercase i, circumflex accent
644 case 0x8e: // lowercase o, circumflex accent
648 case 0x8f: // lowercase u, circumflex accent
652 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
653 // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
654 case 0x90: // capital letter A with acute
658 case 0x91: // capital letter E with acute
662 case 0x92: // capital letter O with acute
666 case 0x93: // capital letter U with acute
670 case 0x94: // capital letter U with diaresis
674 case 0x95: // lowercase letter U with diaeresis
678 case 0x96: // apostrophe
681 case 0x97: // inverted exclamation mark
685 case 0x98: // asterisk
688 case 0x99: // apostrophe (yes, duped). See CCADI source code.
691 case 0x9a: // hyphen-minus
694 case 0x9b: // copyright sign
698 case 0x9c: // Service mark
703 case 0x9d: // Full stop (.)
706 case 0x9e: // Quoatation mark
709 case 0x9f: // Quoatation mark
712 case 0xa0: // uppercase A, grave accent
716 case 0xa1: // uppercase A, circumflex
720 case 0xa2: // uppercase C with cedilla
724 case 0xa3: // uppercase E, grave accent
728 case 0xa4: // uppercase E, circumflex
732 case 0xa5: // capital letter E with diaresis
736 case 0xa6: // lowercase letter e with diaresis
740 case 0xa7: // uppercase I, circumflex
744 case 0xa8: // uppercase I, with diaresis
748 case 0xa9: // lowercase i, with diaresis
752 case 0xaa: // uppercase O, circumflex
756 case 0xab: // uppercase U, grave accent
760 case 0xac: // lowercase u, grave accent
764 case 0xad: // uppercase U, circumflex
768 case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
772 case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
776 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
777 // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
778 case 0xb0: // Uppercase A, tilde
782 case 0xb1: // Lowercase a, tilde
786 case 0xb2: // Uppercase I, acute accent
790 case 0xb3: // Uppercase I, grave accent
794 case 0xb4: // Lowercase i, grave accent
798 case 0xb5: // Uppercase O, grave accent
802 case 0xb6: // Lowercase o, grave accent
806 case 0xb7: // Uppercase O, tilde
810 case 0xb8: // Lowercase o, tilde
814 case 0xb9: // Open curly brace
817 case 0xba: // Closing curly brace
820 case 0xbb: // Backslash
826 case 0xbd: // Underscore
829 case 0xbe: // Pipe (broken bar)
834 *buffer=0x7e; // Not sure
836 case 0xc0: // Uppercase A, umlaut
840 case 0xc1: // Lowercase A, umlaut
844 case 0xc2: // Uppercase O, umlaut
848 case 0xc3: // Lowercase o, umlaut
852 case 0xc4: // Esszett (sharp S)
856 case 0xc5: // Yen symbol
860 case 0xc6: // Currency symbol
864 case 0xc7: // Vertical bar
867 case 0xc8: // Uppercase A, ring
871 case 0xc9: // Lowercase A, ring
875 case 0xca: // Uppercase O, slash
879 case 0xcb: // Lowercase o, slash
883 case 0xcc: // Upper left corner
888 case 0xcd: // Upper right corner
893 case 0xce: // Lower left corner
898 case 0xcf: // Lower right corner
904 *buffer='?'; // I'll do it eventually, I promise
905 return 1; // This are weird chars anyway
909 unsigned char cctolower (unsigned char c)
911 if (c>='A' && c<='Z')
915 case 0x7d: // uppercase N tilde
917 case 0x90: // capital letter A with acute
919 case 0x91: // capital letter E with acute
921 case 0x92: // capital letter O with acute
923 case 0x93: // capital letter U with acute
925 case 0xa2: // uppercase C with cedilla
927 case 0xa0: // uppercase A, grave accent
929 case 0xa3: // uppercase E, grave accent
931 case 0xa1: // uppercase A, circumflex
933 case 0xa4: // uppercase E, circumflex
935 case 0xa7: // uppercase I, circumflex
937 case 0xaa: // uppercase O, circumflex
939 case 0xad: // uppercase U, circumflex
941 case 0x94: // capital letter U with diaresis
943 case 0xa5: // capital letter E with diaresis
945 case 0xa8: // uppercase I, with diaresis
947 case 0xab: // uppercase U, grave accent
949 case 0xb0: // Uppercase A, tilde
951 case 0xb2: // Uppercase I, acute accent
953 case 0xb3: // Uppercase I, grave accent
955 case 0xb5: // Uppercase O, grave accent
957 case 0xb7: // Uppercase O, tilde
959 case 0xc0: // Uppercase A, umlaut
961 case 0xc2: // Uppercase O, umlaut
963 case 0xc8: // Uppercase A, ring
965 case 0xca: // Uppercase O, slash
971 unsigned char cctoupper (unsigned char c)
973 if (c>='a' && c<='z')
977 case 0x7e: // lowercase n tilde
979 case 0x2a: // lowercase a, acute accent
981 case 0x5c: // lowercase e, acute accent
983 case 0x5e: // lowercase i, acute accent
985 case 0x5f: // lowercase o, acute accent
987 case 0x60: // lowercase u, acute accent
989 case 0x7b: // lowercase c with cedilla
991 case 0x88: // lowercase a, grave accent
993 case 0x8a: // lowercase e, grave accent
995 case 0x8b: // lowercase a, circumflex accent
997 case 0x8c: // lowercase e, circumflex accent
999 case 0x8d: // lowercase i, circumflex accent
1001 case 0x8e: // lowercase o, circumflex accent
1003 case 0x8f: // lowercase u, circumflex accent
1005 case 0x95: // lowercase letter U with diaeresis
1007 case 0xa6: // lowercase letter e with diaresis
1009 case 0xa9: // lowercase i, with diaresis
1011 case 0xac: // lowercase u, grave accent
1013 case 0xb1: // Lowercase a, tilde
1015 case 0xb4: // Lowercase i, grave accent
1017 case 0xb6: // Lowercase o, grave accent
1019 case 0xb8: // Lowercase o, tilde
1021 case 0xc1: // Lowercase A, umlaut
1023 case 0xc3: // Lowercase o, umlaut
1025 case 0xc9: // Lowercase A, ring
1027 case 0xcb: // Lowercase o, slash
1034 // Encodes a generic string. Note that since we use the encoders for closed caption
1035 // data, text would have to be encoded as CCs... so using special characters here
1037 unsigned encode_line (unsigned char *buffer, unsigned char *text)
1062 #define ISSEPARATOR(c) (c==' ' || c==0x89 || ispunct(c) \
1063 || c==0x99) // This is the apostrofe. We get it here in CC encoding, not ASCII
1066 void correct_case (int line_num, struct eia608_screen *data)
1069 /* while (i<spell_words) */
1071 /* char *c=(char *) data->characters[line_num]; */
1072 /* size_t len=strlen (spell_correct[i]); */
1073 /* while ((c=strstr (c,spell_lower[i]))!=NULL) */
1075 /* // Make sure it's a whole word (start of line or */
1076 /* // preceded by space, and end of line or followed by */
1078 /* unsigned char prev; */
1079 /* if (c==(char *) data->characters[line_num]) // Beginning of line... */
1080 /* prev=' '; // ...Pretend we had a blank before */
1083 /* unsigned char next; */
1084 /* if (c-(char *) data->characters[line_num]+len==CC608_SCREEN_WIDTH) // End of line... */
1085 /* next=' '; // ... pretend we have a blank later */
1087 /* next=*(c+len); */
1088 /* if ( ISSEPARATOR(prev) && ISSEPARATOR(next)) */
1090 /* memcpy (c,spell_correct[i],len); */
1098 void capitalize (int line_num, struct eia608_screen *data, int *new_sentence)
1102 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1104 switch (data->characters[line_num][i])
1107 case 0x89: // This is a transparent space
1110 case '.': // Fallthrough
1111 case '?': // Fallthrough
1118 data->characters[line_num][i]=cctoupper (data->characters[line_num][i]);
1120 data->characters[line_num][i]=cctolower (data->characters[line_num][i]);
1127 void find_limit_characters (unsigned char *line, int *first_non_blank, int *last_non_blank)
1132 *first_non_blank=-1;
1133 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1135 unsigned char c=line[i];
1136 if (c!=' ' && c!=0x89)
1138 if (*first_non_blank==-1)
1145 unsigned get_decoder_line_basic (unsigned char *buffer, int line_num, struct eia608_screen *data)
1147 unsigned char *line = data->characters[line_num];
1148 int last_non_blank=-1;
1149 int first_non_blank=-1;
1150 unsigned char *orig=buffer; // Keep for debugging
1152 find_limit_characters (line, &first_non_blank, &last_non_blank);
1154 if (first_non_blank==-1)
1161 for (i=first_non_blank;i<=last_non_blank;i++)
1167 bytes=get_char_in_utf_8 (buffer,c);
1170 get_char_in_latin_1 (buffer,c);
1174 get_char_in_unicode (buffer,c);
1181 return (unsigned) (buffer-orig); // Return length
1184 unsigned get_decoder_line_encoded_for_gui (unsigned char *buffer, int line_num, struct eia608_screen *data)
1186 unsigned char *line = data->characters[line_num];
1187 unsigned char *orig=buffer; // Keep for debugging
1188 int first=0, last=31;
1191 find_limit_characters(line,&first,&last);
1192 for (i=first;i<=last;i++)
1194 get_char_in_latin_1 (buffer,line[i]);
1198 return (unsigned) (buffer-orig); // Return length
1202 unsigned get_decoder_line_encoded (unsigned char *buffer, int line_num, struct eia608_screen *data)
1204 int col = COL_WHITE;
1209 unsigned char *line = data->characters[line_num];
1210 unsigned char *orig=buffer; // Keep for debugging
1211 int first=0, last=31;
1213 find_limit_characters(line,&first,&last);
1214 for (i=first;i<=last;i++)
1217 int its_col = data->colors[line_num][i];
1218 if (its_col != col && !nofontcolor)
1220 if (col!=COL_WHITE) // We need to close the previous font tag
1222 buffer+= encode_line (buffer,(unsigned char *) "</font>");
1225 buffer+=encode_line (buffer, (unsigned char*) color_text[its_col][1]);
1226 if (its_col==COL_USERDEFINED)
1228 // The previous sentence doesn't copy the whole
1229 // <font> tag, just up to the quote before the color
1230 buffer+=encode_line (buffer, (unsigned char*) usercolor_rgb);
1231 buffer+=encode_line (buffer, (unsigned char*) "\">");
1236 // Handle underlined
1237 int is_underlined = data->fonts[line_num][i] & FONT_UNDERLINED;
1238 if (is_underlined && underlined==0) // Open underline
1240 buffer+=encode_line (buffer, (unsigned char *) "<u>");
1242 if (is_underlined==0 && underlined) // Close underline
1244 buffer+=encode_line (buffer, (unsigned char *) "</u>");
1246 underlined=is_underlined;
1248 int has_ita = data->fonts[line_num][i] & FONT_ITALICS;
1249 if (has_ita && italics==0) // Open italics
1251 buffer+=encode_line (buffer, (unsigned char *) "<i>");
1253 if (has_ita==0 && italics) // Close italics
1255 buffer+=encode_line (buffer, (unsigned char *) "</i>");
1262 bytes=get_char_in_utf_8 (buffer,line[i]);
1265 get_char_in_latin_1 (buffer,line[i]);
1269 get_char_in_unicode (buffer,line[i]);
1277 buffer+=encode_line (buffer, (unsigned char *) "</i>");
1281 buffer+=encode_line (buffer, (unsigned char *) "</u>");
1283 if (col != COL_WHITE && !nofontcolor)
1285 buffer+=encode_line (buffer, (unsigned char *) "</font>");
1288 return (unsigned) (buffer-orig); // Return length
1292 void delete_all_lines_but_current (struct eia608_screen *data, int row)
1299 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
1300 data->characters[i][CC608_SCREEN_WIDTH]=0;
1301 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
1302 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
1303 data->row_used[i]=0;
1308 void clear_eia608_cc_buffer (struct eia608_screen *data)
1314 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
1315 data->characters[i][CC608_SCREEN_WIDTH]=0;
1316 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
1317 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
1318 data->row_used[i]=0;
1323 void init_eia608 (struct eia608 *data)
1325 data->cursor_column=0;
1327 clear_eia608_cc_buffer (&data->buffer1);
1328 clear_eia608_cc_buffer (&data->buffer2);
1329 data->visible_buffer=1;
1332 data->mode=MODE_POPUP;
1333 // data->current_visible_start_cc=0;
1334 data->current_visible_start_ms=0;
1335 data->srt_counter=0;
1336 data->screenfuls_counter=0;
1338 data->color=default_color;
1339 data->font=FONT_REGULAR;
1340 data->rollup_base_row=14;
1343 struct eia608_screen *get_writing_buffer (struct s_write *wb)
1345 struct eia608_screen *use_buffer=NULL;
1346 switch (wb->data608->mode)
1348 case MODE_POPUP: // Write on the non-visible buffer
1349 if (wb->data608->visible_buffer==1)
1350 use_buffer = &wb->data608->buffer2;
1352 use_buffer = &wb->data608->buffer1;
1354 case MODE_ROLLUP_2: // Write directly to screen
1357 if (wb->data608->visible_buffer==1)
1358 use_buffer = &wb->data608->buffer1;
1360 use_buffer = &wb->data608->buffer2;
1363 fatal (EXIT_BUG_BUG, "Caption mode has an illegal value at get_writing_buffer(), this is a bug.\n");
1368 void write_char (const unsigned char c, struct s_write *wb)
1370 if (wb->data608->mode!=MODE_TEXT)
1372 struct eia608_screen * use_buffer=get_writing_buffer(wb);
1373 /* hb_log ("\rWriting char [%c] at %s:%d:%d\n",c,
1374 use_buffer == &wb->data608->buffer1?"B1":"B2",
1375 wb->data608->cursor_row,wb->data608->cursor_column); */
1376 use_buffer->characters[wb->data608->cursor_row][wb->data608->cursor_column]=c;
1377 use_buffer->colors[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->color;
1378 use_buffer->fonts[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->font;
1379 use_buffer->row_used[wb->data608->cursor_row]=1;
1380 use_buffer->empty=0;
1381 if (wb->data608->cursor_column<31)
1382 wb->data608->cursor_column++;
1387 /* Handle MID-ROW CODES. */
1388 void handle_text_attr (const unsigned char c1, const unsigned char c2, struct s_write *wb)
1390 // Handle channel change
1391 wb->data608->channel=wb->new_channel;
1392 if (wb->data608->channel!=cc_channel)
1395 hb_log ("\r608: text_attr: %02X %02X",c1,c2);
1396 if ( ((c1!=0x11 && c1!=0x19) ||
1397 (c2<0x20 || c2>0x2f)) && debug_608)
1399 hb_log ("\rThis is not a text attribute!\n");
1404 wb->data608->color=pac2_attribs[i][0];
1405 wb->data608->font=pac2_attribs[i][1];
1407 hb_log (" -- Color: %s, font: %s\n",
1408 color_text[wb->data608->color][0],
1409 font_text[wb->data608->font]);
1410 if (wb->data608->cursor_column<31)
1411 wb->data608->cursor_column++;
1415 void mstotime (LLONG milli, unsigned *hours, unsigned *minutes,
1416 unsigned *seconds, unsigned *ms)
1418 // LLONG milli = (LLONG) ((ccblock*1000)/29.97);
1419 *ms=(unsigned) (milli%1000); // milliseconds
1420 milli=(milli-*ms)/1000; // Remainder, in seconds
1421 *seconds = (int) (milli%60);
1422 milli=(milli-*seconds)/60; // Remainder, in minutes
1423 *minutes = (int) (milli%60);
1424 milli=(milli-*minutes)/60; // Remainder, in hours
1428 void write_subtitle_file_footer (struct s_write *wb)
1430 switch (write_format)
1433 sprintf ((char *) str,"</BODY></SAMI>\n");
1434 if (debug_608 && encoding!=ENC_UNICODE)
1436 hb_log ("\r%s\n", str);
1438 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1439 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1440 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1442 default: // Nothing to do. Only SAMI has a footer
1447 void fhb_log_encoded (FILE *fh, const char *string)
1449 //GUARANTEE(wb, strlen (string)*3);
1450 //wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) string);
1451 //fwrite (wb->enc_buffer,wb->enc_buffer_used,1,fh);
1454 void write_subtitle_file_header (struct s_write *wb)
1456 switch (write_format)
1458 case OF_SRT: // Subrip subtitles have no header
1460 case OF_SAMI: // This header brought to you by McPoodle's CCASDI
1461 //fhb_log_encoded (wb->fh, sami_header);
1462 GUARANTEE(wb, strlen (sami_header)*3);
1463 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) sami_header);
1464 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1465 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1467 case OF_RCWT: // Write header
1468 //fwrite (rcwt_header, sizeof(rcwt_header),1,wb->fh);
1470 case OF_TRANSCRIPT: // No header. Fall thru
1476 void write_cc_line_as_transcript (struct eia608_screen *data, struct s_write *wb, int line_number)
1478 hb_buffer_t *buffer;
1482 capitalize(line_number,data, &wb->new_sentence);
1483 correct_case(line_number,data);
1485 int length = get_decoder_line_basic (wb->subline, line_number, data);
1486 if (debug_608 && encoding!=ENC_UNICODE)
1489 hb_log ("%s\n",wb->subline);
1493 //fwrite (wb->subline, 1, length, wb->fh);
1495 * Put this subtitle in a hb_buffer_t and shove it into the subtitle fifo
1497 buffer = hb_buffer_init( length + 1 );
1498 buffer->start = wb->data608->current_visible_start_ms;
1499 buffer->stop = get_fts(wb);
1500 memcpy( buffer->data, wb->subline, length + 1 );
1501 //hb_log("CC %lld: %s", buffer->stop, wb->subline);
1503 if (wb->hb_last_buffer) {
1504 wb->hb_last_buffer->next = buffer;
1506 wb->hb_buffer = buffer;
1508 wb->hb_last_buffer = buffer;
1510 XMLRPC_APPEND(wb->subline,length);
1511 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1512 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1514 // fhb_log (wb->fh,encoded_crlf);
1517 int write_cc_buffer_as_transcript (struct eia608_screen *data, struct s_write *wb)
1521 int wrote_something = 0;
1524 hb_log ("\n- - - TRANSCRIPT caption - - -\n");
1528 if (data->row_used[i])
1530 write_cc_line_as_transcript (data,wb, i);
1536 hb_log ("- - - - - - - - - - - -\r\n");
1538 return wrote_something;
1541 void write_cc_buffer_to_gui (struct eia608_screen *data, struct s_write *wb)
1543 unsigned h1,m1,s1,ms1;
1544 unsigned h2,m2,s2,ms2;
1547 LLONG ms_start= wb->data608->current_visible_start_ms;
1549 ms_start+=subs_delay;
1550 if (ms_start<0) // Drop screens that because of subs_delay start too early
1552 int time_reported=0;
1555 if (data->row_used[i])
1557 hb_log ("###SUBTITLE#");
1560 LLONG ms_end = get_fts(wb)+subs_delay;
1561 mstotime (ms_start,&h1,&m1,&s1,&ms1);
1562 mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
1563 // Note, only MM:SS here as we need to save space in the preview window
1564 hb_log ("%02u:%02u#%02u:%02u#",
1565 h1*60+m1,s1, h2*60+m2,s2);
1571 // We don't capitalize here because whatever function that was used
1572 // before to write to file already took care of it.
1573 int length = get_decoder_line_encoded_for_gui (wb->subline, i, data);
1574 fwrite (wb->subline, 1, length, stderr);
1575 fwrite ("\n",1,1,stderr);
1581 int write_cc_buffer_as_srt (struct eia608_screen *data, struct s_write *wb)
1583 unsigned h1,m1,s1,ms1;
1584 unsigned h2,m2,s2,ms2;
1585 int wrote_something = 0;
1586 LLONG ms_start= wb->data608->current_visible_start_ms;
1589 ms_start+=subs_delay;
1590 if (ms_start<0) // Drop screens that because of subs_delay start too early
1593 LLONG ms_end = get_fts(wb)+subs_delay;
1594 mstotime (ms_start,&h1,&m1,&s1,&ms1);
1595 mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
1597 wb->data608->srt_counter++;
1598 sprintf (timeline,"%u\r\n",wb->data608->srt_counter);
1599 //wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) timeline);
1600 //fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1601 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1602 //sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n",
1603 // h1,m1,s1,ms1, h2,m2,s2,ms2);
1604 //wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) timeline);
1607 hb_log ("\n- - - SRT caption - - -\n");
1610 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1611 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1614 * Write all the lines into enc_buffer, and then write that out at the end
1615 * ensure that we only have two lines, insert a newline after the first one,
1616 * and have a big bottom line (strip spaces from any joined lines).
1621 if (data->row_used[i])
1625 capitalize(i,data, &wb->new_sentence);
1626 correct_case(i,data);
1629 * The intention was to use a newline but QT doesn't like it, old code still
1630 * here just in case..
1633 wb->enc_buffer_used = get_decoder_line_encoded (wb->enc_buffer, i, data);
1637 wb->enc_buffer_used += encode_line (wb->enc_buffer+wb->enc_buffer_used,
1638 (unsigned char *) " ");
1640 wb->enc_buffer_used += encode_line (wb->enc_buffer+wb->enc_buffer_used,
1641 (unsigned char *) " ");
1643 wb->enc_buffer_used += get_decoder_line_encoded (wb->enc_buffer+wb->enc_buffer_used, i, data);
1647 if (wb->enc_buffer_used)
1649 hb_buffer_t *buffer = hb_buffer_init( wb->enc_buffer_used + 2 );
1650 buffer->start = ms_start;
1651 buffer->stop = ms_end;
1652 memcpy( buffer->data, wb->enc_buffer, wb->enc_buffer_used + 1 );
1653 if (wb->hb_last_buffer) {
1654 wb->hb_last_buffer->next = buffer;
1656 wb->hb_buffer = buffer;
1658 wb->hb_last_buffer = buffer;
1664 hb_log ("- - - - - - - - - - - -\r\n");
1666 // fhb_log (wb->fh, encoded_crlf);
1667 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1668 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1669 return wrote_something;
1672 int write_cc_buffer_as_sami (struct eia608_screen *data, struct s_write *wb)
1674 int wrote_something=0;
1675 LLONG startms = wb->data608->current_visible_start_ms;
1678 startms+=subs_delay;
1679 if (startms<0) // Drop screens that because of subs_delay start too early
1682 LLONG endms = get_fts(wb)+subs_delay;
1683 endms--; // To prevent overlapping with next line.
1684 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\">\r\n",startms);
1685 if (debug_608 && encoding!=ENC_UNICODE)
1687 hb_log ("\r%s\n", str);
1689 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1690 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1691 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1694 if (data->row_used[i])
1696 int length = get_decoder_line_encoded (wb->subline, i, data);
1697 if (debug_608 && encoding!=ENC_UNICODE)
1700 hb_log ("%s\n",wb->subline);
1702 fwrite (wb->subline, 1, length, wb->fh);
1703 XMLRPC_APPEND(wb->subline,length);
1707 fwrite (encoded_br, 1, encoded_br_length,wb->fh);
1708 XMLRPC_APPEND(encoded_br, encoded_br_length);
1710 fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1711 XMLRPC_APPEND(encoded_crlf, encoded_crlf_length);
1714 sprintf ((char *) str,"</P></SYNC>\r\n");
1715 if (debug_608 && encoding!=ENC_UNICODE)
1717 hb_log ("\r%s\n", str);
1719 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1720 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1721 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1722 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",endms);
1723 if (debug_608 && encoding!=ENC_UNICODE)
1725 hb_log ("\r%s\n", str);
1727 wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) str);
1728 fwrite (wb->enc_buffer,wb->enc_buffer_used,1,wb->fh);
1729 XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used);
1730 return wrote_something;
1733 struct eia608_screen *get_current_visible_buffer (struct s_write *wb)
1735 struct eia608_screen *data;
1736 if (wb->data608->visible_buffer==1)
1737 data = &wb->data608->buffer1;
1739 data = &wb->data608->buffer2;
1744 int write_cc_buffer (struct s_write *wb)
1746 struct eia608_screen *data;
1747 int wrote_something=0;
1748 if (screens_to_process!=-1 && wb->data608->screenfuls_counter>=screens_to_process)
1754 if (wb->data608->visible_buffer==1)
1755 data = &wb->data608->buffer1;
1757 data = &wb->data608->buffer2;
1762 switch (write_format)
1765 wrote_something = write_cc_buffer_as_srt (data, wb);
1768 wrote_something = write_cc_buffer_as_sami (data,wb);
1771 wrote_something = write_cc_buffer_as_transcript (data,wb);
1776 if (wrote_something && gui_mode_reports)
1777 write_cc_buffer_to_gui (data,wb);
1779 return wrote_something;
1782 void roll_up(struct s_write *wb)
1784 struct eia608_screen *use_buffer;
1787 if (wb->data608->visible_buffer==1)
1788 use_buffer = &wb->data608->buffer1;
1790 use_buffer = &wb->data608->buffer2;
1792 switch (wb->data608->mode)
1803 default: // Shouldn't happen
1807 int firstrow=-1, lastrow=-1;
1808 // Look for the last line used
1809 int rows_now=0; // Number of rows in use right now
1812 if (use_buffer->row_used[i])
1822 hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow);
1824 if (lastrow==-1) // Empty screen, nothing to rollup
1827 for (j=lastrow-keep_lines+1;j<lastrow; j++)
1831 memcpy (use_buffer->characters[j],use_buffer->characters[j+1],CC608_SCREEN_WIDTH+1);
1832 memcpy (use_buffer->colors[j],use_buffer->colors[j+1],CC608_SCREEN_WIDTH+1);
1833 memcpy (use_buffer->fonts[j],use_buffer->fonts[j+1],CC608_SCREEN_WIDTH+1);
1834 use_buffer->row_used[j]=use_buffer->row_used[j+1];
1837 for (j=0;j<(1+wb->data608->cursor_row-keep_lines);j++)
1839 memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH);
1840 memset(use_buffer->colors[j],COL_WHITE,CC608_SCREEN_WIDTH);
1841 memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH);
1842 use_buffer->characters[j][CC608_SCREEN_WIDTH]=0;
1843 use_buffer->row_used[j]=0;
1845 memset(use_buffer->characters[lastrow],' ',CC608_SCREEN_WIDTH);
1846 memset(use_buffer->colors[lastrow],COL_WHITE,CC608_SCREEN_WIDTH);
1847 memset(use_buffer->fonts[lastrow],FONT_REGULAR,CC608_SCREEN_WIDTH);
1849 use_buffer->characters[lastrow][CC608_SCREEN_WIDTH]=0;
1850 use_buffer->row_used[lastrow]=0;
1855 if (use_buffer->row_used[i])
1857 if (rows_now>keep_lines)
1858 hb_log ("Bug in roll_up, should have %d lines but I have %d.\n",
1859 keep_lines, rows_now);
1862 void erase_memory (struct s_write *wb, int displayed)
1864 struct eia608_screen *buf;
1867 if (wb->data608->visible_buffer==1)
1868 buf=&wb->data608->buffer1;
1870 buf=&wb->data608->buffer2;
1874 if (wb->data608->visible_buffer==1)
1875 buf=&wb->data608->buffer2;
1877 buf=&wb->data608->buffer1;
1879 clear_eia608_cc_buffer (buf);
1882 int is_current_row_empty (struct s_write *wb)
1884 struct eia608_screen *use_buffer;
1887 if (wb->data608->visible_buffer==1)
1888 use_buffer = &wb->data608->buffer1;
1890 use_buffer = &wb->data608->buffer2;
1891 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1893 if (use_buffer->characters[wb->data608->rollup_base_row][i]!=' ')
1899 /* Process GLOBAL CODES */
1900 void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct s_write *wb)
1902 // Handle channel change
1903 wb->data608->channel=wb->new_channel;
1904 if (wb->data608->channel!=cc_channel)
1907 enum command_code command = COM_UNKNOWN;
1910 if ((c1==0x14 || c1==0x1C) && c2==0x2C)
1911 command = COM_ERASEDISPLAYEDMEMORY;
1912 if ((c1==0x14 || c1==0x1C) && c2==0x20)
1913 command = COM_RESUMECAPTIONLOADING;
1914 if ((c1==0x14 || c1==0x1C) && c2==0x2F)
1915 command = COM_ENDOFCAPTION;
1916 if ((c1==0x17 || c1==0x1F) && c2==0x21)
1917 command = COM_TABOFFSET1;
1918 if ((c1==0x17 || c1==0x1F) && c2==0x22)
1919 command = COM_TABOFFSET2;
1920 if ((c1==0x17 || c1==0x1F) && c2==0x23)
1921 command = COM_TABOFFSET3;
1922 if ((c1==0x14 || c1==0x1C) && c2==0x25)
1923 command = COM_ROLLUP2;
1924 if ((c1==0x14 || c1==0x1C) && c2==0x26)
1925 command = COM_ROLLUP3;
1926 if ((c1==0x14 || c1==0x1C) && c2==0x27)
1927 command = COM_ROLLUP4;
1928 if ((c1==0x14 || c1==0x1C) && c2==0x2D)
1929 command = COM_CARRIAGERETURN;
1930 if ((c1==0x14 || c1==0x1C) && c2==0x2E)
1931 command = COM_ERASENONDISPLAYEDMEMORY;
1932 if ((c1==0x14 || c1==0x1C) && c2==0x21)
1933 command = COM_BACKSPACE;
1934 if ((c1==0x14 || c1==0x1C) && c2==0x2b)
1935 command = COM_RESUMETEXTDISPLAY;
1938 hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]);
1943 if (wb->data608->cursor_column>0)
1945 wb->data608->cursor_column--;
1946 get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' ';
1949 case COM_TABOFFSET1:
1950 if (wb->data608->cursor_column<31)
1951 wb->data608->cursor_column++;
1953 case COM_TABOFFSET2:
1954 wb->data608->cursor_column+=2;
1955 if (wb->data608->cursor_column>31)
1956 wb->data608->cursor_column=31;
1958 case COM_TABOFFSET3:
1959 wb->data608->cursor_column+=3;
1960 if (wb->data608->cursor_column>31)
1961 wb->data608->cursor_column=31;
1963 case COM_RESUMECAPTIONLOADING:
1964 wb->data608->mode=MODE_POPUP;
1966 case COM_RESUMETEXTDISPLAY:
1967 wb->data608->mode=MODE_TEXT;
1970 if (wb->data608->mode==MODE_POPUP)
1972 if (write_cc_buffer (wb))
1973 wb->data608->screenfuls_counter++;
1974 erase_memory (wb, 1);
1976 if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb))
1979 hb_log ("Two RU2, current line not empty. Simulating a CR\n");
1980 handle_command(0x14, 0x2D, wb);
1982 wb->data608->mode=MODE_ROLLUP_2;
1983 erase_memory (wb, 0);
1984 wb->data608->cursor_column=0;
1985 wb->data608->cursor_row=wb->data608->rollup_base_row;
1988 if (wb->data608->mode==MODE_POPUP)
1990 if (write_cc_buffer (wb))
1991 wb->data608->screenfuls_counter++;
1992 erase_memory (wb, 1);
1994 if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb))
1997 hb_log ("Two RU3, current line not empty. Simulating a CR\n");
1998 handle_command(0x14, 0x2D, wb);
2000 wb->data608->mode=MODE_ROLLUP_3;
2001 erase_memory (wb, 0);
2002 wb->data608->cursor_column=0;
2003 wb->data608->cursor_row=wb->data608->rollup_base_row;
2006 if (wb->data608->mode==MODE_POPUP)
2008 if (write_cc_buffer (wb))
2009 wb->data608->screenfuls_counter++;
2010 erase_memory (wb, 1);
2012 if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb))
2015 hb_log ("Two RU4, current line not empty. Simulating a CR\n");
2016 handle_command(0x14, 0x2D, wb);
2019 wb->data608->mode=MODE_ROLLUP_4;
2020 wb->data608->cursor_column=0;
2021 wb->data608->cursor_row=wb->data608->rollup_base_row;
2022 erase_memory (wb, 0);
2024 case COM_CARRIAGERETURN:
2025 // In transcript mode, CR doesn't write the whole screen, to avoid
2027 if (write_format==OF_TRANSCRIPT)
2029 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2034 delete_all_lines_but_current (get_current_visible_buffer (wb), wb->data608->cursor_row);
2035 if (write_cc_buffer(wb))
2036 wb->data608->screenfuls_counter++;
2039 wb->data608->current_visible_start_ms=get_fts(wb);
2040 wb->data608->cursor_column=0;
2042 case COM_ERASENONDISPLAYEDMEMORY:
2043 erase_memory (wb,0);
2045 case COM_ERASEDISPLAYEDMEMORY:
2046 // Write it to disk before doing this, and make a note of the new
2047 // time it became clear.
2048 if (write_format==OF_TRANSCRIPT &&
2049 (wb->data608->mode==MODE_ROLLUP_2 || wb->data608->mode==MODE_ROLLUP_3 ||
2050 wb->data608->mode==MODE_ROLLUP_4))
2052 // In transcript mode we just write the cursor line. The previous lines
2053 // should have been written already, so writing everything produces
2055 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2059 if (write_cc_buffer (wb))
2060 wb->data608->screenfuls_counter++;
2062 erase_memory (wb,1);
2063 wb->data608->current_visible_start_ms=get_fts(wb);
2065 case COM_ENDOFCAPTION: // Switch buffers
2066 // The currently *visible* buffer is leaving, so now we know it's ending
2067 // time. Time to actually write it to file.
2068 if (write_cc_buffer (wb))
2069 wb->data608->screenfuls_counter++;
2070 wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1;
2071 wb->data608->current_visible_start_ms=get_fts(wb);
2072 wb->data608->cursor_column=0;
2073 wb->data608->cursor_row=0;
2074 wb->data608->color=default_color;
2075 wb->data608->font=FONT_REGULAR;
2080 hb_log ("\rNot yet implemented.\n");
2086 void handle_end_of_data (struct s_write *wb)
2088 // We issue a EraseDisplayedMemory here so if there's any captions pending
2089 // they get written to file.
2090 handle_command (0x14, 0x2c, wb); // EDM
2093 void handle_double (const unsigned char c1, const unsigned char c2, struct s_write *wb)
2096 if (wb->data608->channel!=cc_channel)
2098 if (c2>=0x30 && c2<=0x3f)
2100 c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here
2102 hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c);
2107 /* Process EXTENDED CHARACTERS */
2108 unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_write *wb)
2110 // Handle channel change
2111 if (wb->new_channel > 2)
2113 wb->new_channel -= 2;
2115 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2117 wb->data608->channel=wb->new_channel;
2118 if (wb->data608->channel!=cc_channel)
2121 // For lo values between 0x20-0x3f
2125 hb_log ("\rExtended: %02X %02X\n",hi,lo);
2126 if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13))
2131 c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here
2134 c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here
2137 // This column change is because extended characters replace
2138 // the previous character (which is sent for basic decoders
2139 // to show something similar to the real char)
2140 if (wb->data608->cursor_column>0)
2141 wb->data608->cursor_column--;
2148 /* Process PREAMBLE ACCESS CODES (PAC) */
2149 void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb)
2151 // Handle channel change
2152 if (wb->new_channel > 2)
2154 wb->new_channel -= 2;
2156 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2158 wb->data608->channel=wb->new_channel;
2159 if (wb->data608->channel!=cc_channel)
2162 int row=rowdata[((c1<<1)&14)|((c2>>5)&1)];
2165 hb_log ("\rPAC: %02X %02X",c1,c2);
2167 if (c2>=0x40 && c2<=0x5f)
2173 if (c2>=0x60 && c2<=0x7f)
2180 hb_log ("\rThis is not a PAC!!!!!\n");
2184 int color=pac2_attribs[c2][0];
2185 int font=pac2_attribs[c2][1];
2186 int indent=pac2_attribs[c2][2];
2188 hb_log (" -- Position: %d:%d, color: %s, font: %s\n",row,
2189 indent,color_text[color][0],font_text[font]);
2190 if (wb->data608->mode!=MODE_TEXT)
2192 // According to Robson, row info is discarded in text mode
2193 // but column is accepted
2194 wb->data608->cursor_row=row-1 ; // Since the array is 0 based
2196 wb->data608->rollup_base_row=row-1;
2197 wb->data608->cursor_column=indent;
2201 void handle_single (const unsigned char c1, struct s_write *wb)
2203 if (c1<0x20 || wb->data608->channel!=cc_channel)
2204 return; // We don't allow special stuff here
2209 hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */
2213 int check_channel (unsigned char c1, struct s_write *wb)
2217 if (debug_608 && wb->data608->channel!=1)
2218 hb_log ("\nChannel change, now 1\n");
2223 if (debug_608 && wb->data608->channel!=2)
2224 hb_log ("\nChannel change, now 2\n");
2229 if (debug_608 && wb->data608->channel!=3)
2230 hb_log ("\nChannel change, now 3\n");
2235 if (debug_608 && wb->data608->channel!=4)
2236 hb_log ("\nChannel change, now 4\n");
2240 // Otherwise keep the current channel
2241 return wb->data608->channel;
2244 /* Handle Command, special char or attribute and also check for
2246 * Returns 1 if something was written to screen, 0 otherwise */
2247 int disCommand (unsigned char hi, unsigned char lo, struct s_write *wb)
2249 int wrote_to_screen=0;
2251 /* Full channel changes are only allowed for "GLOBAL CODES",
2252 * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES",
2254 * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and
2255 * SPECIAL/SPECIAL CHARACTERS allow only switching
2256 * between 1&3 or 2&4. */
2257 wb->new_channel = check_channel (hi,wb);
2258 //if (wb->data608->channel!=cc_channel)
2261 if (hi>=0x18 && hi<=0x1f)
2267 if (lo>=0x40 && lo<=0x5f)
2268 handle_pac (hi,lo,wb);
2271 if (lo>=0x20 && lo<=0x2f)
2272 handle_text_attr (hi,lo,wb);
2273 if (lo>=0x30 && lo<=0x3f)
2276 handle_double (hi,lo,wb);
2278 if (lo>=0x40 && lo<=0x7f)
2279 handle_pac (hi,lo,wb);
2283 if (lo>=0x20 && lo<=0x3f)
2285 wrote_to_screen=handle_extended (hi,lo,wb);
2287 if (lo>=0x40 && lo<=0x7f)
2288 handle_pac (hi,lo,wb);
2292 if (lo>=0x20 && lo<=0x2f)
2293 handle_command (hi,lo,wb);
2294 if (lo>=0x40 && lo<=0x7f)
2295 handle_pac (hi,lo,wb);
2298 if (lo>=0x40 && lo<=0x7f)
2299 handle_pac (hi,lo,wb);
2302 if (lo>=0x21 && lo<=0x22)
2303 handle_command (hi,lo,wb);
2304 if (lo>=0x2e && lo<=0x2f)
2305 handle_text_attr (hi,lo,wb);
2306 if (lo>=0x40 && lo<=0x7f)
2307 handle_pac (hi,lo,wb);
2310 return wrote_to_screen;
2313 void process608 (const unsigned char *data, int length, struct s_write *wb)
2315 static int textprinted = 0;
2320 for (i=0;i<length;i=i+2)
2322 unsigned char hi, lo;
2323 int wrote_to_screen=0;
2324 hi = data[i] & 0x7F; // Get rid of parity bit
2325 lo = data[i+1] & 0x7F; // Get rid of parity bit
2327 if (hi==0 && lo==0) // Just padding
2329 // hb_log ("\r[%02X:%02X]\n",hi,lo);
2331 if (hi>=0x01 && hi<=0x0E)
2333 // XDS crap - mode. Would be nice to support it eventually
2334 // wb->data608->last_c1=0;
2335 // wb->data608->last_c2=0;
2336 wb->data608->channel=3; // Always channel 3
2339 if (hi==0x0F) // End of XDS block
2344 if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char
2345 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
2346 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
2348 // We were writing characters before, start a new line for
2349 // diagnostic output from disCommand()
2350 if (debug_608 && textprinted == 1 )
2356 wb->in_xds_mode=0; // Back to normal
2357 if (wb->data608->last_c1==hi && wb->data608->last_c2==lo)
2359 // Duplicate dual code, discard
2362 wb->data608->last_c1=hi;
2363 wb->data608->last_c2=lo;
2364 wrote_to_screen=disCommand (hi,lo,wb);
2366 if (hi>=0x20) // Standard characters (always in pairs)
2368 // Only print if the channel is active
2369 if (wb->data608->channel!=cc_channel)
2374 if( textprinted == 0 )
2381 handle_single(hi,wb);
2382 handle_single(lo,wb);
2384 wb->data608->last_c1=0;
2385 wb->data608->last_c2=0;
2388 if ( debug_608 && !textprinted && wb->data608->channel==cc_channel )
2389 { // Current FTS information after the characters are shown
2390 //hb_log("Current FTS: %s\n", print_mstime(get_fts()));
2393 if (wrote_to_screen && direct_rollup && // If direct_rollup is enabled and
2394 (wb->data608->mode==MODE_ROLLUP_2 || // we are in rollup mode, write now.
2395 wb->data608->mode==MODE_ROLLUP_3 ||
2396 wb->data608->mode==MODE_ROLLUP_4))
2398 // We don't increase screenfuls_counter here.
2399 write_cc_buffer (wb);
2400 wb->data608->current_visible_start_ms=get_fts(wb);
2407 /* Return a pointer to a string that holds the printable characters
2408 * of the caption data block. FOR DEBUG PURPOSES ONLY! */
2409 unsigned char *debug_608toASC (unsigned char *cc_data, int channel)
2411 static unsigned char output[3];
2413 unsigned char cc_valid = (cc_data[0] & 4) >>2;
2414 unsigned char cc_type = cc_data[0] & 3;
2415 unsigned char hi, lo;
2421 if (cc_valid && cc_type==channel)
2423 hi = cc_data[1] & 0x7F; // Get rid of parity bit
2424 lo = cc_data[2] & 0x7F; // Get rid of parity bit
2428 output[1]=(lo>=20 ? lo : '.');
2442 struct hb_work_private_s
2445 struct s_write * cc608;
2448 int decccInit( hb_work_object_t * w, hb_job_t * job )
2451 hb_work_private_t * pv;
2453 pv = calloc( 1, sizeof( hb_work_private_t ) );
2456 w->private_data = pv;
2460 pv->cc608 = calloc(1, sizeof(struct s_write));
2464 retval = general_608_init(pv->cc608);
2467 pv->cc608->data608 = calloc(1, sizeof(struct eia608));
2468 if( !pv->cc608->data608 )
2478 int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
2479 hb_buffer_t ** buf_out )
2481 hb_work_private_t * pv = w->private_data;
2482 hb_buffer_t * in = *buf_in;
2484 if ( in->size <= 0 )
2486 /* EOF on input stream - send it downstream & say that we're done */
2487 handle_end_of_data(pv->cc608);
2489 * Grab any pending buffer and output them with the EOF on the end
2491 if (pv->cc608->hb_last_buffer) {
2492 pv->cc608->hb_last_buffer->next = in;
2493 *buf_out = pv->cc608->hb_buffer;
2495 pv->cc608->hb_buffer = NULL;
2496 pv->cc608->hb_last_buffer = NULL;
2501 return HB_WORK_DONE;
2504 pv->cc608->last_pts = in->start;
2506 process608(in->data, in->size, pv->cc608);
2509 * If there is one waiting then pass it on
2511 *buf_out = pv->cc608->hb_buffer;
2512 pv->cc608->hb_buffer = NULL;
2513 pv->cc608->hb_last_buffer = NULL;
2518 void decccClose( hb_work_object_t * w )
2520 hb_work_private_t * pv = w->private_data;
2521 general_608_close( pv->cc608 );
2522 free( pv->cc608->data608 );
2524 free( w->private_data );
2527 hb_work_object_t hb_deccc608 =
2530 "Closed Caption (608) decoder",