WinGui: Fix another instance of the Caliburn vs Json.net sillyness where objects...
[HandBrake.git] / libhb / deccc608sub.c
blob3b3f953932a1f947e173789b9189e018b98be037
1 /* deccc608sub.c
3 Copyright (c) 2003-2015 HandBrake Team
4 This file is part of the HandBrake source code
5 Homepage: <http://handbrake.fr/>.
6 It may be used under the terms of the GNU General Public License v2.
7 For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
8 */
11 * From ccextractor, leave this file as intact and close to the original as possible so that
12 * it is easy to patch in fixes - even though this file contains code that we don't need.
14 * Note that the SRT sub generation from CC could be useful for mkv subs.
16 #include "hb.h"
17 #include "deccc608sub.h"
19 #define SSA_PREAMBLE_LEN 24
21 * ccextractor static configuration variables.
23 static int debug_608 = 0;
24 static int cc_channel = 1;
25 static int subs_delay = 0;
28 * Get the time of the last buffer that we have received.
30 static int64_t get_last_pts(struct s_write *wb)
32 return wb->last_pts;
35 #define fatal(N, ...) // N
37 int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10};
38 // Relationship between the first PAC byte and the row number
40 // The following enc_buffer is not used at the moment, if it does get used
41 // we need to bring it into the swrite struct. Same for "str".
42 #define INITIAL_ENC_BUFFER_CAPACITY 2048
44 static const unsigned char pac2_attribs[][3]= // Color, font, ident
46 {COL_WHITE, FONT_REGULAR, 0}, // 0x40 || 0x60
47 {COL_WHITE, FONT_UNDERLINED, 0}, // 0x41 || 0x61
48 {COL_GREEN, FONT_REGULAR, 0}, // 0x42 || 0x62
49 {COL_GREEN, FONT_UNDERLINED, 0}, // 0x43 || 0x63
50 {COL_BLUE, FONT_REGULAR, 0}, // 0x44 || 0x64
51 {COL_BLUE, FONT_UNDERLINED, 0}, // 0x45 || 0x65
52 {COL_CYAN, FONT_REGULAR, 0}, // 0x46 || 0x66
53 {COL_CYAN, FONT_UNDERLINED, 0}, // 0x47 || 0x67
54 {COL_RED, FONT_REGULAR, 0}, // 0x48 || 0x68
55 {COL_RED, FONT_UNDERLINED, 0}, // 0x49 || 0x69
56 {COL_YELLOW, FONT_REGULAR, 0}, // 0x4a || 0x6a
57 {COL_YELLOW, FONT_UNDERLINED, 0}, // 0x4b || 0x6b
58 {COL_MAGENTA, FONT_REGULAR, 0}, // 0x4c || 0x6c
59 {COL_MAGENTA, FONT_UNDERLINED, 0}, // 0x4d || 0x6d
60 {COL_WHITE, FONT_ITALICS, 0}, // 0x4e || 0x6e
61 {COL_WHITE, FONT_UNDERLINED_ITALICS, 0}, // 0x4f || 0x6f
62 {COL_WHITE, FONT_REGULAR, 0}, // 0x50 || 0x70
63 {COL_WHITE, FONT_UNDERLINED, 0}, // 0x51 || 0x71
64 {COL_WHITE, FONT_REGULAR, 4}, // 0x52 || 0x72
65 {COL_WHITE, FONT_UNDERLINED, 4}, // 0x53 || 0x73
66 {COL_WHITE, FONT_REGULAR, 8}, // 0x54 || 0x74
67 {COL_WHITE, FONT_UNDERLINED, 8}, // 0x55 || 0x75
68 {COL_WHITE, FONT_REGULAR, 12}, // 0x56 || 0x76
69 {COL_WHITE, FONT_UNDERLINED, 12}, // 0x57 || 0x77
70 {COL_WHITE, FONT_REGULAR, 16}, // 0x58 || 0x78
71 {COL_WHITE, FONT_UNDERLINED, 16}, // 0x59 || 0x79
72 {COL_WHITE, FONT_REGULAR, 20}, // 0x5a || 0x7a
73 {COL_WHITE, FONT_UNDERLINED, 20}, // 0x5b || 0x7b
74 {COL_WHITE, FONT_REGULAR, 24}, // 0x5c || 0x7c
75 {COL_WHITE, FONT_UNDERLINED, 24}, // 0x5d || 0x7d
76 {COL_WHITE, FONT_REGULAR, 28}, // 0x5e || 0x7e
77 {COL_WHITE, FONT_UNDERLINED, 28} // 0x5f || 0x7f
80 // Default color
81 static enum color_code default_color=COL_WHITE;
83 static const char *command_type[] =
85 "Unknown",
86 "EDM - EraseDisplayedMemory",
87 "RCL - ResumeCaptionLoading",
88 "EOC - End Of Caption",
89 "TO1 - Tab Offset, 1 column",
90 "TO2 - Tab Offset, 2 column",
91 "TO3 - Tab Offset, 3 column",
92 "RU2 - Roll up 2 rows",
93 "RU3 - Roll up 3 rows",
94 "RU4 - Roll up 4 rows",
95 "CR - Carriage Return",
96 "ENM - Erase non-displayed memory",
97 "BS - Backspace",
98 "RTD - Resume Text Display"
101 static const char *font_text[]=
103 "regular",
104 "italics",
105 "underlined",
106 "underlined italics"
109 static const char *color_text[][2]=
111 {"white", "&HFFFFFF&"},
112 {"green", "&H00FF00&"},
113 {"blue", "&HFF0000&"},
114 {"cyan", "&HFFFF00&"},
115 {"red", "&H0000FF&"},
116 {"yellow", "&H00FFFF&"},
117 {"magenta", "&HFF00FF&"},
118 {"userdefined", "&HFFFFFF&"}
121 static int general_608_init (struct s_write *wb)
123 if( !wb->enc_buffer )
125 wb->enc_buffer=(unsigned char *) malloc (INITIAL_ENC_BUFFER_CAPACITY);
126 if (wb->enc_buffer==NULL)
127 return -1;
128 wb->enc_buffer_capacity=INITIAL_ENC_BUFFER_CAPACITY;
131 if( !wb->subline) {
132 wb->subline = malloc(2048);
134 if (!wb->subline)
136 return -1;
140 wb->new_sentence = 1;
141 wb->new_channel = 1;
142 wb->in_xds_mode = 0;
144 wb->hb_buffer = NULL;
145 wb->hb_last_buffer = NULL;
146 wb->last_pts = 0;
147 return 0;
151 * Free up CC memory - don't call this from HB just yet since it will cause
152 * parallel encodes to fail - to be honest they will be stuffed anyway since
153 * the CC's may be overwriting the buffers.
155 static void general_608_close (struct s_write *wb)
157 if( wb->enc_buffer ) {
158 free(wb->enc_buffer);
159 wb->enc_buffer_capacity = 0;
160 wb->enc_buffer_used = 0;
162 if( wb->subline ) {
163 free(wb->subline);
166 if( wb->hb_buffer ) {
167 hb_buffer_close( &wb->hb_buffer );
172 #include <ctype.h>
174 // Returns number of bytes used
175 static int get_char_in_utf8(unsigned char *buffer, unsigned char c)
177 if (c == 0x00)
178 return 0;
180 // Regular line-21 character set, mostly ASCII except these exceptions
181 if (c < 0x80)
183 switch (c)
185 case 0x2a: // lowercase a, acute accent
186 *buffer = 0xc3;
187 *(buffer+1) = 0xa1;
188 return 2;
189 case 0x5c: // lowercase e, acute accent
190 *buffer = 0xc3;
191 *(buffer+1) = 0xa9;
192 return 2;
193 case 0x5e: // lowercase i, acute accent
194 *buffer = 0xc3;
195 *(buffer+1) = 0xad;
196 return 2;
197 case 0x5f: // lowercase o, acute accent
198 *buffer = 0xc3;
199 *(buffer+1) = 0xb3;
200 return 2;
201 case 0x60: // lowercase u, acute accent
202 *buffer = 0xc3;
203 *(buffer+1) = 0xba;
204 return 2;
205 case 0x7b: // lowercase c with cedilla
206 *buffer = 0xc3;
207 *(buffer+1) = 0xa7;
208 return 2;
209 case 0x7c: // division symbol
210 *buffer = 0xc3;
211 *(buffer+1) = 0xb7;
212 return 2;
213 case 0x7d: // uppercase N tilde
214 *buffer = 0xc3;
215 *(buffer+1) = 0x91;
216 return 2;
217 case 0x7e: // lowercase n tilde
218 *buffer = 0xc3;
219 *(buffer+1) = 0xb1;
220 return 2;
221 default:
222 *buffer = c;
223 return 1;
226 switch (c)
228 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
229 // THAT COME FROM HI BYTE = 0x11 AND LOW BETWEEN 0x30 AND 0x3F
230 case 0x80: // Registered symbol (R)
231 *buffer = 0xc2;
232 *(buffer+1) = 0xae;
233 return 2;
234 case 0x81: // degree sign
235 *buffer = 0xc2;
236 *(buffer+1) = 0xb0;
237 return 2;
238 case 0x82: // 1/2 symbol
239 *buffer = 0xc2;
240 *(buffer+1) = 0xbd;
241 return 2;
242 case 0x83: // Inverted (open) question mark
243 *buffer = 0xc2;
244 *(buffer+1) = 0xbf;
245 return 2;
246 case 0x84: // Trademark symbol (TM)
247 *buffer = 0xe2;
248 *(buffer+1) = 0x84;
249 *(buffer+2) = 0xa2;
250 return 3;
251 case 0x85: // Cents symbol
252 *buffer = 0xc2;
253 *(buffer+1) = 0xa2;
254 return 2;
255 case 0x86: // Pounds sterling
256 *buffer = 0xc2;
257 *(buffer+1) = 0xa3;
258 return 2;
259 case 0x87: // Music note
260 *buffer = 0xe2;
261 *(buffer+1) = 0x99;
262 *(buffer+2) = 0xaa;
263 return 3;
264 case 0x88: // lowercase a, grave accent
265 *buffer = 0xc3;
266 *(buffer+1) = 0xa0;
267 return 2;
268 case 0x89: // transparent space, we make it regular
269 *buffer = 0x20;
270 return 1;
271 case 0x8a: // lowercase e, grave accent
272 *buffer = 0xc3;
273 *(buffer+1) = 0xa8;
274 return 2;
275 case 0x8b: // lowercase a, circumflex accent
276 *buffer = 0xc3;
277 *(buffer+1) = 0xa2;
278 return 2;
279 case 0x8c: // lowercase e, circumflex accent
280 *buffer = 0xc3;
281 *(buffer+1) = 0xaa;
282 return 2;
283 case 0x8d: // lowercase i, circumflex accent
284 *buffer = 0xc3;
285 *(buffer+1) = 0xae;
286 return 2;
287 case 0x8e: // lowercase o, circumflex accent
288 *buffer = 0xc3;
289 *(buffer+1) = 0xb4;
290 return 2;
291 case 0x8f: // lowercase u, circumflex accent
292 *buffer = 0xc3;
293 *(buffer+1) = 0xbb;
294 return 2;
295 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
296 // THAT COME FROM HI BYTE = 0x12 AND LOW BETWEEN 0x20 AND 0x3F
297 case 0x90: // capital letter A with acute
298 *buffer = 0xc3;
299 *(buffer+1) = 0x81;
300 return 2;
301 case 0x91: // capital letter E with acute
302 *buffer = 0xc3;
303 *(buffer+1) = 0x89;
304 return 2;
305 case 0x92: // capital letter O with acute
306 *buffer = 0xc3;
307 *(buffer+1) = 0x93;
308 return 2;
309 case 0x93: // capital letter U with acute
310 *buffer = 0xc3;
311 *(buffer+1) = 0x9a;
312 return 2;
313 case 0x94: // capital letter U with diaresis
314 *buffer = 0xc3;
315 *(buffer+1) = 0x9c;
316 return 2;
317 case 0x95: // lowercase letter U with diaeresis
318 *buffer = 0xc3;
319 *(buffer+1) = 0xbc;
320 return 2;
321 case 0x96: // apostrophe
322 *buffer = 0x27;
323 return 1;
324 case 0x97: // inverted exclamation mark
325 *buffer = 0xc2;
326 *(buffer+1) = 0xa1;
327 return 2;
328 case 0x98: // asterisk
329 *buffer = 0x2a;
330 return 1;
331 case 0x99: // apostrophe (yes, duped). See CCADI source code.
332 *buffer = 0x27;
333 return 1;
334 case 0x9a: // hyphen-minus
335 *buffer = 0x2d;
336 return 1;
337 case 0x9b: // copyright sign
338 *buffer = 0xc2;
339 *(buffer+1) = 0xa9;
340 return 2;
341 case 0x9c: // Service mark
342 *buffer = 0xe2;
343 *(buffer+1) = 0x84;
344 *(buffer+2) = 0xa0;
345 return 3;
346 case 0x9d: // Full stop (.)
347 *buffer = 0x2e;
348 return 1;
349 case 0x9e: // Quoatation mark
350 *buffer = 0x22;
351 return 1;
352 case 0x9f: // Quoatation mark
353 *buffer = 0x22;
354 return 1;
355 case 0xa0: // uppercase A, grave accent
356 *buffer = 0xc3;
357 *(buffer+1) = 0x80;
358 return 2;
359 case 0xa1: // uppercase A, circumflex
360 *buffer = 0xc3;
361 *(buffer+1) = 0x82;
362 return 2;
363 case 0xa2: // uppercase C with cedilla
364 *buffer = 0xc3;
365 *(buffer+1) = 0x87;
366 return 2;
367 case 0xa3: // uppercase E, grave accent
368 *buffer = 0xc3;
369 *(buffer+1) = 0x88;
370 return 2;
371 case 0xa4: // uppercase E, circumflex
372 *buffer = 0xc3;
373 *(buffer+1) = 0x8a;
374 return 2;
375 case 0xa5: // capital letter E with diaresis
376 *buffer = 0xc3;
377 *(buffer+1) = 0x8b;
378 return 2;
379 case 0xa6: // lowercase letter e with diaresis
380 *buffer = 0xc3;
381 *(buffer+1) = 0xab;
382 return 2;
383 case 0xa7: // uppercase I, circumflex
384 *buffer = 0xc3;
385 *(buffer+1) = 0x8e;
386 return 2;
387 case 0xa8: // uppercase I, with diaresis
388 *buffer = 0xc3;
389 *(buffer+1) = 0x8f;
390 return 2;
391 case 0xa9: // lowercase i, with diaresis
392 *buffer = 0xc3;
393 *(buffer+1) = 0xaf;
394 return 2;
395 case 0xaa: // uppercase O, circumflex
396 *buffer = 0xc3;
397 *(buffer+1) = 0x94;
398 return 2;
399 case 0xab: // uppercase U, grave accent
400 *buffer = 0xc3;
401 *(buffer+1) = 0x99;
402 return 2;
403 case 0xac: // lowercase u, grave accent
404 *buffer = 0xc3;
405 *(buffer+1) = 0xb9;
406 return 2;
407 case 0xad: // uppercase U, circumflex
408 *buffer = 0xc3;
409 *(buffer+1) = 0x9b;
410 return 2;
411 case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
412 *buffer = 0xc2;
413 *(buffer+1) = 0xab;
414 return 2;
415 case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
416 *buffer = 0xc2;
417 *(buffer+1) = 0xbb;
418 return 2;
419 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
420 // THAT COME FROM HI BYTE = 0x13 AND LOW BETWEEN 0x20 AND 0x3F
421 case 0xb0: // Uppercase A, tilde
422 *buffer = 0xc3;
423 *(buffer+1) = 0x83;
424 return 2;
425 case 0xb1: // Lowercase a, tilde
426 *buffer = 0xc3;
427 *(buffer+1) = 0xa3;
428 return 2;
429 case 0xb2: // Uppercase I, acute accent
430 *buffer = 0xc3;
431 *(buffer+1) = 0x8d;
432 return 2;
433 case 0xb3: // Uppercase I, grave accent
434 *buffer = 0xc3;
435 *(buffer+1) = 0x8c;
436 return 2;
437 case 0xb4: // Lowercase i, grave accent
438 *buffer = 0xc3;
439 *(buffer+1) = 0xac;
440 return 2;
441 case 0xb5: // Uppercase O, grave accent
442 *buffer = 0xc3;
443 *(buffer+1) = 0x92;
444 return 2;
445 case 0xb6: // Lowercase o, grave accent
446 *buffer = 0xc3;
447 *(buffer+1) = 0xb2;
448 return 2;
449 case 0xb7: // Uppercase O, tilde
450 *buffer = 0xc3;
451 *(buffer+1) = 0x95;
452 return 2;
453 case 0xb8: // Lowercase o, tilde
454 *buffer = 0xc3;
455 *(buffer+1) = 0xb5;
456 return 2;
457 case 0xb9: // Open curly brace
458 *buffer = 0x7b;
459 return 1;
460 case 0xba: // Closing curly brace
461 *buffer = 0x7d;
462 return 1;
463 case 0xbb: // Backslash
464 *buffer = 0x5c;
465 return 1;
466 case 0xbc: // Caret
467 *buffer = 0x5e;
468 return 1;
469 case 0xbd: // Underscore
470 *buffer = 0x5f;
471 return 1;
472 case 0xbe: // Pipe (broken bar)
473 *buffer = 0xc2;
474 *(buffer+1) = 0xa6;
475 return 1;
476 case 0xbf: // Tilde
477 *buffer = 0x7e; // Not sure
478 return 1;
479 case 0xc0: // Uppercase A, umlaut
480 *buffer = 0xc3;
481 *(buffer+1) = 0x84;
482 return 2;
483 case 0xc1: // Lowercase A, umlaut
484 *buffer = 0xc3;
485 *(buffer+1) = 0xa4;
486 return 2;
487 case 0xc2: // Uppercase O, umlaut
488 *buffer = 0xc3;
489 *(buffer+1) = 0x96;
490 return 2;
491 case 0xc3: // Lowercase o, umlaut
492 *buffer = 0xc3;
493 *(buffer+1) = 0xb6;
494 return 2;
495 case 0xc4: // Esszett (sharp S)
496 *buffer = 0xc3;
497 *(buffer+1) = 0x9f;
498 return 2;
499 case 0xc5: // Yen symbol
500 *buffer = 0xc2;
501 *(buffer+1) = 0xa5;
502 return 2;
503 case 0xc6: // Currency symbol
504 *buffer = 0xc2;
505 *(buffer+1) = 0xa4;
506 return 2;
507 case 0xc7: // Vertical bar
508 *buffer = 0x7c;
509 return 1;
510 case 0xc8: // Uppercase A, ring
511 *buffer = 0xc3;
512 *(buffer+1) = 0x85;
513 return 2;
514 case 0xc9: // Lowercase A, ring
515 *buffer = 0xc3;
516 *(buffer+1) = 0xa5;
517 return 2;
518 case 0xca: // Uppercase O, slash
519 *buffer = 0xc3;
520 *(buffer+1) = 0x98;
521 return 2;
522 case 0xcb: // Lowercase o, slash
523 *buffer = 0xc3;
524 *(buffer+1) = 0xb8;
525 return 2;
526 case 0xcc: // Upper left corner
527 *buffer = 0xe2;
528 *(buffer+1) = 0x8c;
529 *(buffer+2) = 0x9c;
530 return 3;
531 case 0xcd: // Upper right corner
532 *buffer = 0xe2;
533 *(buffer+1) = 0x8c;
534 *(buffer+2) = 0x9d;
535 return 3;
536 case 0xce: // Lower left corner
537 *buffer = 0xe2;
538 *(buffer+1) = 0x8c;
539 *(buffer+2) = 0x9e;
540 return 3;
541 case 0xcf: // Lower right corner
542 *buffer = 0xe2;
543 *(buffer+1) = 0x8c;
544 *(buffer+2) = 0x9f;
545 return 3;
546 default: //
547 *buffer = '?'; // I'll do it eventually, I promise
548 return 1; // This are weird chars anyway
552 // Encodes a generic string. Note that since we use the encoders for closed
553 // caption data, text would have to be encoded as CCs... so using special
554 // characters here it's a bad idea.
555 static unsigned encode_line(unsigned char *buffer, unsigned char *text)
557 unsigned bytes = 0;
558 while (*text)
560 *buffer++ = *text++;
561 bytes++;
563 return bytes;
566 static unsigned stuff_space(unsigned char *buffer, int space)
568 int ii;
569 for (ii = 0; ii < space; ii++)
571 *buffer++ = '\\';
572 *buffer++ = 'h';
574 return space * 2;
577 static void find_limit_characters(unsigned char *line, int *first_non_blank,
578 int *last_non_blank)
580 int i;
582 *last_non_blank = -1;
583 *first_non_blank = -1;
584 for (i = 0; i < CC608_SCREEN_WIDTH; i++)
586 unsigned char c = line[i];
587 if (c != ' ' && c != 0x89)
589 if (*first_non_blank == -1)
590 *first_non_blank = i;
591 *last_non_blank = i;
596 static unsigned get_decoder_line_encoded(struct s_write *wb,
597 unsigned char *buffer, int line_num,
598 struct eia608_screen *data)
600 uint8_t font_style;
601 uint8_t font_color;
602 int i;
604 unsigned char *line = data->characters[line_num];
605 unsigned char *orig = buffer; // Keep for debugging
606 int first = 0, last = 31;
608 find_limit_characters(line, &first, &last);
609 for (i = first; i <= last; i++)
611 // Handle color
612 font_color = data->colors[line_num][i];
613 font_style = data->fonts[line_num][i];
615 // Handle reset to defaults
616 if ((font_style & FONT_STYLE_MASK) == 0 && font_color == COL_WHITE)
618 if (((font_style ^ wb->prev_font_style) & FONT_STYLE_MASK) ||
619 (font_color != wb->prev_font_color))
621 buffer += encode_line(buffer, (uint8_t*)"{\\r}");
624 else
626 // Open markup
627 if (((font_style ^ wb->prev_font_style) & FONT_STYLE_MASK) ||
628 (font_color != wb->prev_font_color))
630 // style changed
631 buffer += encode_line(buffer, (uint8_t*)"{");
634 // Handle underlined
635 if ((font_style ^ wb->prev_font_style) & FONT_UNDERLINED)
637 int enable = !!(font_style & FONT_UNDERLINED);
638 buffer += encode_line(buffer, (uint8_t*)"\\u");
639 *buffer++ = enable + 0x30;
642 // Handle italics
643 if ((font_style ^ wb->prev_font_style) & FONT_ITALICS)
645 int enable = !!(font_style & FONT_ITALICS);
646 buffer += encode_line(buffer, (uint8_t*)"\\i");
647 *buffer++ = enable + 0x30;
650 // Handle color
651 if (font_color != wb->prev_font_color)
653 buffer += encode_line(buffer, (uint8_t*)"\\1c");
654 buffer += encode_line(buffer,
655 (uint8_t*)color_text[font_color][1]);
658 // Close markup
659 if (((font_style ^ wb->prev_font_style) & FONT_STYLE_MASK) ||
660 (font_color != wb->prev_font_color))
662 // style changed
663 buffer += encode_line(buffer, (uint8_t*)"}");
666 wb->prev_font_style = font_style;
667 wb->prev_font_color = font_color;
669 int bytes = 0;
670 bytes = get_char_in_utf8(buffer, line[i]);
671 buffer += bytes;
673 *buffer = 0;
674 return (unsigned) (buffer - orig); // Return length
678 static void clear_eia608_cc_buffer (struct eia608_screen *data)
680 int i;
682 for (i=0;i<15;i++)
684 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
685 data->characters[i][CC608_SCREEN_WIDTH]=0;
686 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
687 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
688 data->row_used[i]=0;
690 data->empty=1;
693 static void init_eia608 (struct eia608 *data)
695 data->cursor_column = 0;
696 data->cursor_row = 0;
697 clear_eia608_cc_buffer (&data->buffer1);
698 clear_eia608_cc_buffer (&data->buffer2);
699 data->visible_buffer = 1;
700 data->last_c1 = 0;
701 data->last_c2 = 0;
702 data->mode = MODE_POPUP;
703 data->current_visible_start_ms = 0;
704 data->ssa_counter = 0;
705 data->screenfuls_counter = 0;
706 data->channel = 1;
707 data->color = default_color;
708 data->font = FONT_REGULAR;
709 data->rollup_base_row = 14;
712 static struct eia608_screen *get_current_hidden_buffer(struct s_write *wb)
714 struct eia608_screen *data;
715 if (wb->data608->visible_buffer == 1)
716 data = &wb->data608->buffer2;
717 else
718 data = &wb->data608->buffer1;
719 return data;
722 static struct eia608_screen *get_current_visible_buffer(struct s_write *wb)
724 struct eia608_screen *data;
725 if (wb->data608->visible_buffer == 1)
726 data = &wb->data608->buffer1;
727 else
728 data = &wb->data608->buffer2;
729 return data;
732 static void swap_visible_buffer(struct s_write *wb)
734 wb->data608->visible_buffer = (wb->data608->visible_buffer == 1) ? 2 : 1;
737 static struct eia608_screen *get_writing_buffer(struct s_write *wb)
739 struct eia608_screen *use_buffer=NULL;
740 switch (wb->data608->mode)
742 case MODE_POPUP: // Write on the non-visible buffer
743 use_buffer = get_current_hidden_buffer(wb);
744 break;
745 case MODE_ROLLUP_2: // Write directly to screen
746 case MODE_ROLLUP_3:
747 case MODE_ROLLUP_4:
748 use_buffer = get_current_visible_buffer(wb);
749 break;
750 default:
751 fatal (EXIT_BUG_BUG, "Caption mode has an illegal value at get_writing_buffer(), this is a bug.\n");
753 return use_buffer;
756 static void write_char(const unsigned char c, struct s_write *wb)
758 if (wb->data608->mode != MODE_TEXT)
760 struct eia608_screen * use_buffer = get_writing_buffer(wb);
761 /* hb_log ("\rWriting char [%c] at %s:%d:%d\n",c,
762 use_buffer == &wb->data608->buffer1?"B1":"B2",
763 wb->data608->cursor_row,wb->data608->cursor_column); */
764 use_buffer->characters[wb->data608->cursor_row][wb->data608->cursor_column] = c;
765 use_buffer->colors[wb->data608->cursor_row][wb->data608->cursor_column] = wb->data608->color;
766 use_buffer->fonts[wb->data608->cursor_row][wb->data608->cursor_column] = wb->data608->font;
767 use_buffer->row_used[wb->data608->cursor_row] = 1;
768 use_buffer->empty = 0;
769 if (wb->data608->cursor_column < 31)
770 wb->data608->cursor_column++;
771 use_buffer->dirty = 1;
776 /* Handle MID-ROW CODES. */
777 static void handle_text_attr(const unsigned char c1, const unsigned char c2,
778 struct s_write *wb)
780 // Handle channel change
781 wb->data608->channel=wb->new_channel;
782 if (wb->data608->channel!=cc_channel)
783 return;
784 if (debug_608)
785 hb_log ("\r608: text_attr: %02X %02X",c1,c2);
786 if ( ((c1!=0x11 && c1!=0x19) ||
787 (c2<0x20 || c2>0x2f)) && debug_608)
789 hb_log ("\rThis is not a text attribute!\n");
791 else
793 int i = c2-0x20;
794 wb->data608->color=pac2_attribs[i][0];
795 wb->data608->font=pac2_attribs[i][1];
796 if (debug_608)
797 hb_log(" -- Color: %s, font: %s\n",
798 color_text[wb->data608->color][0],
799 font_text[wb->data608->font]);
800 if (wb->data608->cursor_column<31)
801 wb->data608->cursor_column++;
805 static int write_cc_buffer_as_ssa(struct eia608_screen *data,
806 struct s_write *wb)
808 int wrote_something = 0;
809 int i;
810 int64_t ms_start = wb->data608->current_visible_start_ms;
811 //int64_t ms_end = get_last_pts(wb) + subs_delay;
813 ms_start += subs_delay;
814 if (ms_start<0) // Drop screens that because of subs_delay start too early
815 return 0;
817 if (debug_608)
819 char timeline[128];
820 wb->data608->ssa_counter++;
821 sprintf (timeline,"%u\r\n",wb->data608->ssa_counter);
823 hb_log ("\n- - - SSA caption - - -\n");
824 hb_log ("%s", timeline);
828 * Write all the lines into enc_buffer, and then write that out at the end
829 * ensure that we only have two lines, insert a newline after the first one,
830 * and have a big bottom line (strip spaces from any joined lines).
832 int rows = 0, columns = 0;
833 int min_row = 15, max_row = 0;
834 int min_col = 41, max_col = 0;
835 for (i = 0; i < 15; i++)
837 if (data->row_used[i])
839 int first, last;
841 rows++;
842 find_limit_characters(data->characters[i], &first, &last);
843 if (last - first + 1 > columns)
844 columns = last - first + 1;
845 if (min_col > first)
846 min_col = first;
847 if (min_row > i)
848 min_row = i;
849 if (max_col < last)
850 max_col = last;
851 if (max_row < i)
852 max_row = i;
856 wb->prev_font_style = FONT_REGULAR;
857 wb->prev_font_color = COL_WHITE;
858 wb->enc_buffer_used = 0;
860 int cropped_width, cropped_height, font_size;
861 int cell_width, cell_height;
862 int safe_x, safe_y;
863 int min_safe_x, min_safe_y;
864 double aspect;
866 cropped_height = wb->height - wb->crop[0] - wb->crop[1];
867 cropped_width = wb->width - wb->crop[2] - wb->crop[3];
868 aspect = (double)wb->width * wb->par.num /
869 (wb->height * wb->par.den);
871 // CC grid is 16 rows by 32 colums (for 4:3 video)
872 // Our SSA resolution is the title resolution
873 // Tranlate CC grid to SSA coordinates
874 // The numbers are tweaked to keep things off the very
875 // edges of the screen and in the "safe" zone
876 int screen_columns = 32;
877 if (aspect >= 1.6)
879 // If the display aspect is close to or greater than 16:9
880 // then width of screen is 42 columns (see CEA-708)
881 screen_columns = 42;
883 font_size = wb->height * .8 * .08;
885 safe_x = 0.1 * wb->width;
886 safe_y = 0.1 * wb->height;
887 min_safe_x = 0.025 * cropped_width;
888 min_safe_y = 0.025 * cropped_height;
889 cell_height = (wb->height - 2 * safe_y) / 16;
890 cell_width = (wb->width - 2 * safe_x) / screen_columns;
892 char *pos;
893 int y, x, top;
894 int col = min_col;
895 if (aspect >= 1.6)
897 // If the display aspect is close to or greater than 16:9
898 // center the CC in about a 4:3 region
899 col += 5;
901 y = cell_height * (min_row + 1 + rows) + safe_y - wb->crop[0];
902 x = cell_width * col + safe_x - wb->crop[2];
903 top = y - rows * font_size;
905 if (top < min_safe_y)
906 y = (rows * font_size) + min_safe_y;
907 if (y > cropped_height - min_safe_y)
908 y = cropped_height - min_safe_y;
909 if (x + columns * cell_width > cropped_width - min_safe_x)
910 x = cropped_width - columns * cell_width - min_safe_x;
911 if (x < min_safe_x)
912 x = min_safe_x;
913 pos = hb_strdup_printf("{\\an1\\pos(%d,%d)}", x, y);
915 int line = 1;
916 for (i = 0; i < 15; i++)
918 if (data->row_used[i])
920 int first, last;
921 // Get position for this CC
922 find_limit_characters(data->characters[i], &first, &last);
925 * The intention was to use a newline but QT doesn't like it,
926 * old code still here just in case..
928 int space = first - min_col;
929 if (line == 1) {
930 wb->enc_buffer_used += encode_line(
931 wb->enc_buffer + wb->enc_buffer_used, (uint8_t*)pos);
932 wb->enc_buffer_used += stuff_space(
933 wb->enc_buffer + wb->enc_buffer_used, space);
934 wb->enc_buffer_used += get_decoder_line_encoded(wb,
935 wb->enc_buffer + wb->enc_buffer_used, i, data);
936 line = 2;
937 } else {
938 wb->enc_buffer_used += encode_line(
939 wb->enc_buffer + wb->enc_buffer_used, (uint8_t*)"\\N");
940 wb->enc_buffer_used += stuff_space(
941 wb->enc_buffer + wb->enc_buffer_used, space);
942 wb->enc_buffer_used += get_decoder_line_encoded(wb,
943 wb->enc_buffer + wb->enc_buffer_used, i, data);
947 free(pos);
948 if (wb->enc_buffer_used && wb->enc_buffer[0] != 0 && data->dirty)
950 hb_buffer_t *buffer;
951 int len;
953 // bump past null terminator
954 wb->enc_buffer_used++;
955 buffer = hb_buffer_init(wb->enc_buffer_used + SSA_PREAMBLE_LEN);
956 buffer->s.frametype = HB_FRAME_SUBTITLE;
957 buffer->s.start = ms_start;
958 buffer->s.stop = AV_NOPTS_VALUE;
959 sprintf((char*)buffer->data, "%d,,Default,,0,0,0,,", ++wb->line);
960 len = strlen((char*)buffer->data);
961 memcpy(buffer->data + len, wb->enc_buffer, wb->enc_buffer_used);
962 if (wb->hb_last_buffer)
964 wb->hb_last_buffer->next = buffer;
966 else
968 wb->hb_buffer = buffer;
970 wb->hb_last_buffer = buffer;
971 wrote_something=1;
972 wb->clear_sub_needed = 1;
974 else if (wb->clear_sub_needed)
976 hb_buffer_t *buffer = hb_buffer_init(1);
977 buffer->s.frametype = HB_FRAME_SUBTITLE;
978 buffer->s.start = ms_start;
979 buffer->s.stop = ms_start;
980 buffer->data[0] = 0;
981 if (wb->hb_last_buffer != NULL)
983 wb->hb_last_buffer->next = buffer;
985 else
987 wb->hb_buffer = buffer;
989 wb->hb_last_buffer = buffer;
990 wb->clear_sub_needed = 0;
992 if (debug_608)
994 hb_log ("- - - - - - - - - - - -\r\n");
996 return wrote_something;
999 static int write_cc_buffer(struct s_write *wb)
1001 struct eia608_screen *data;
1002 int wrote_something=0;
1004 data = get_current_visible_buffer(wb);
1005 if (!data->dirty)
1006 return 0;
1007 wb->new_sentence=1;
1008 wrote_something = write_cc_buffer_as_ssa(data, wb);
1009 data->dirty = 0;
1010 return wrote_something;
1013 static void move_roll_up(struct s_write *wb, int row)
1015 struct eia608_screen *use_buffer;
1016 int ii, src, dst, keep_lines;
1018 switch (wb->data608->mode)
1020 case MODE_ROLLUP_2:
1021 keep_lines = 2;
1022 break;
1023 case MODE_ROLLUP_3:
1024 keep_lines = 3;
1025 break;
1026 case MODE_ROLLUP_4:
1027 keep_lines = 4;
1028 break;
1029 default:
1030 // Not rollup mode, nothing to do
1031 return;
1034 if (row == wb->data608->rollup_base_row)
1036 // base row didn't change, nothing to do
1037 return;
1040 use_buffer = get_current_visible_buffer(wb);
1041 if (row < wb->data608->rollup_base_row)
1043 src = wb->data608->rollup_base_row - keep_lines + 1;
1044 dst = row - keep_lines + 1;
1045 for (ii = 0; ii < keep_lines; ii++)
1047 memcpy(use_buffer->characters[dst], use_buffer->characters[src], CC608_SCREEN_WIDTH+1);
1048 memcpy(use_buffer->colors[dst], use_buffer->colors[src], CC608_SCREEN_WIDTH+1);
1049 memcpy(use_buffer->fonts[dst], use_buffer->fonts[src], CC608_SCREEN_WIDTH+1);
1050 use_buffer->row_used[dst] = use_buffer->row_used[src];
1052 memset(use_buffer->characters[src], ' ', CC608_SCREEN_WIDTH);
1053 memset(use_buffer->colors[src], COL_WHITE, CC608_SCREEN_WIDTH);
1054 memset(use_buffer->fonts[src], FONT_REGULAR, CC608_SCREEN_WIDTH);
1055 use_buffer->characters[src][CC608_SCREEN_WIDTH] = 0;
1056 use_buffer->row_used[src] = 0;
1058 src++;
1059 dst++;
1062 else
1064 src = wb->data608->rollup_base_row;
1065 dst = row;
1066 for (ii = 0; ii < keep_lines; ii++)
1068 memcpy(use_buffer->characters[dst], use_buffer->characters[src], CC608_SCREEN_WIDTH+1);
1069 memcpy(use_buffer->colors[dst], use_buffer->colors[src], CC608_SCREEN_WIDTH+1);
1070 memcpy(use_buffer->fonts[dst], use_buffer->fonts[src], CC608_SCREEN_WIDTH+1);
1071 use_buffer->row_used[dst] = use_buffer->row_used[src];
1073 memset(use_buffer->characters[src], ' ', CC608_SCREEN_WIDTH);
1074 memset(use_buffer->colors[src], COL_WHITE, CC608_SCREEN_WIDTH);
1075 memset(use_buffer->fonts[src], FONT_REGULAR, CC608_SCREEN_WIDTH);
1076 use_buffer->characters[src][CC608_SCREEN_WIDTH] = 0;
1077 use_buffer->row_used[src] = 0;
1079 src--;
1080 dst--;
1083 use_buffer->dirty = 1;
1086 static void roll_up(struct s_write *wb)
1088 struct eia608_screen *use_buffer;
1089 int i, j;
1091 use_buffer = get_current_visible_buffer(wb);
1092 int keep_lines;
1093 switch (wb->data608->mode)
1095 case MODE_ROLLUP_2:
1096 keep_lines = 2;
1097 break;
1098 case MODE_ROLLUP_3:
1099 keep_lines = 3;
1100 break;
1101 case MODE_ROLLUP_4:
1102 keep_lines = 4;
1103 break;
1104 default: // Shouldn't happen
1105 keep_lines = 0;
1106 break;
1108 int firstrow = -1, lastrow = -1;
1109 // Look for the last line used
1110 int rows_now = 0; // Number of rows in use right now
1111 for (i = 0; i < 15; i++)
1113 if (use_buffer->row_used[i])
1115 rows_now++;
1116 if (firstrow == -1)
1117 firstrow = i;
1118 lastrow = i;
1122 if (debug_608)
1123 hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow);
1125 if (lastrow==-1) // Empty screen, nothing to rollup
1126 return;
1128 for (j = lastrow - keep_lines + 1; j < lastrow; j++)
1130 if (j >= 0)
1132 memcpy(use_buffer->characters[j], use_buffer->characters[j+1], CC608_SCREEN_WIDTH+1);
1133 memcpy(use_buffer->colors[j], use_buffer->colors[j+1], CC608_SCREEN_WIDTH+1);
1134 memcpy(use_buffer->fonts[j], use_buffer->fonts[j+1], CC608_SCREEN_WIDTH+1);
1135 use_buffer->row_used[j] = use_buffer->row_used[j+1];
1138 for (j = 0; j < (1 + wb->data608->cursor_row - keep_lines); j++)
1140 memset(use_buffer->characters[j], ' ', CC608_SCREEN_WIDTH);
1141 memset(use_buffer->colors[j], COL_WHITE, CC608_SCREEN_WIDTH);
1142 memset(use_buffer->fonts[j], FONT_REGULAR, CC608_SCREEN_WIDTH);
1143 use_buffer->characters[j][CC608_SCREEN_WIDTH] = 0;
1144 use_buffer->row_used[j] = 0;
1146 memset(use_buffer->characters[lastrow], ' ', CC608_SCREEN_WIDTH);
1147 memset(use_buffer->colors[lastrow], COL_WHITE, CC608_SCREEN_WIDTH);
1148 memset(use_buffer->fonts[lastrow], FONT_REGULAR, CC608_SCREEN_WIDTH);
1150 use_buffer->characters[lastrow][CC608_SCREEN_WIDTH] = 0;
1151 use_buffer->row_used[lastrow] = 0;
1153 // Sanity check
1154 rows_now = 0;
1155 for (i = 0; i < 15; i++)
1156 if (use_buffer->row_used[i])
1157 rows_now++;
1158 if (rows_now > keep_lines)
1159 hb_log ("Bug in roll_up, should have %d lines but I have %d.\n",
1160 keep_lines, rows_now);
1161 use_buffer->dirty = 1;
1164 void erase_memory (struct s_write *wb, int displayed)
1166 struct eia608_screen *buf;
1167 if (displayed)
1169 buf = get_current_visible_buffer(wb);
1171 else
1173 buf = get_current_hidden_buffer(wb);
1175 clear_eia608_cc_buffer (buf);
1178 static int is_current_row_empty (struct s_write *wb)
1180 struct eia608_screen *use_buffer;
1181 int i;
1183 use_buffer = get_current_visible_buffer(wb);
1184 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1186 if (use_buffer->characters[wb->data608->rollup_base_row][i]!=' ')
1187 return 0;
1189 return 1;
1192 /* Process GLOBAL CODES */
1193 static void handle_command(unsigned char c1, const unsigned char c2,
1194 struct s_write *wb)
1196 // Handle channel change
1197 wb->data608->channel=wb->new_channel;
1198 if (wb->data608->channel!=cc_channel)
1199 return;
1201 enum command_code command = COM_UNKNOWN;
1202 if (c1==0x15)
1203 c1=0x14;
1204 if ((c1==0x14 || c1==0x1C) && c2==0x2C)
1205 command = COM_ERASEDISPLAYEDMEMORY;
1206 if ((c1==0x14 || c1==0x1C) && c2==0x20)
1207 command = COM_RESUMECAPTIONLOADING;
1208 if ((c1==0x14 || c1==0x1C) && c2==0x2F)
1209 command = COM_ENDOFCAPTION;
1210 if ((c1==0x17 || c1==0x1F) && c2==0x21)
1211 command = COM_TABOFFSET1;
1212 if ((c1==0x17 || c1==0x1F) && c2==0x22)
1213 command = COM_TABOFFSET2;
1214 if ((c1==0x17 || c1==0x1F) && c2==0x23)
1215 command = COM_TABOFFSET3;
1216 if ((c1==0x14 || c1==0x1C) && c2==0x25)
1217 command = COM_ROLLUP2;
1218 if ((c1==0x14 || c1==0x1C) && c2==0x26)
1219 command = COM_ROLLUP3;
1220 if ((c1==0x14 || c1==0x1C) && c2==0x27)
1221 command = COM_ROLLUP4;
1222 if ((c1==0x14 || c1==0x1C) && c2==0x2D)
1223 command = COM_CARRIAGERETURN;
1224 if ((c1==0x14 || c1==0x1C) && c2==0x2E)
1225 command = COM_ERASENONDISPLAYEDMEMORY;
1226 if ((c1==0x14 || c1==0x1C) && c2==0x21)
1227 command = COM_BACKSPACE;
1228 if ((c1==0x14 || c1==0x1C) && c2==0x2b)
1229 command = COM_RESUMETEXTDISPLAY;
1230 if (debug_608)
1232 hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]);
1234 switch (command)
1236 case COM_BACKSPACE:
1237 if (wb->data608->cursor_column>0)
1239 struct eia608_screen *data;
1240 data = get_writing_buffer(wb);
1241 wb->data608->cursor_column--;
1242 data->characters[wb->data608->cursor_row][wb->data608->cursor_column] = ' ';
1243 data->dirty = 1;
1245 break;
1246 case COM_TABOFFSET1:
1247 if (wb->data608->cursor_column<31)
1248 wb->data608->cursor_column++;
1249 break;
1250 case COM_TABOFFSET2:
1251 wb->data608->cursor_column+=2;
1252 if (wb->data608->cursor_column>31)
1253 wb->data608->cursor_column=31;
1254 break;
1255 case COM_TABOFFSET3:
1256 wb->data608->cursor_column+=3;
1257 if (wb->data608->cursor_column>31)
1258 wb->data608->cursor_column=31;
1259 break;
1260 case COM_RESUMECAPTIONLOADING:
1261 wb->data608->mode=MODE_POPUP;
1262 wb->data608->current_visible_start_ms = get_last_pts(wb);
1263 break;
1264 case COM_RESUMETEXTDISPLAY:
1265 wb->data608->mode=MODE_TEXT;
1266 wb->data608->current_visible_start_ms = get_last_pts(wb);
1267 break;
1268 case COM_ROLLUP2:
1269 if (wb->data608->rollup_base_row + 1 < 2)
1271 move_roll_up(wb, 1);
1272 wb->data608->rollup_base_row = 1;
1274 if (wb->data608->mode==MODE_POPUP)
1276 swap_visible_buffer(wb);
1277 if (write_cc_buffer(wb))
1278 wb->data608->screenfuls_counter++;
1279 erase_memory (wb, 1);
1281 wb->data608->color=default_color;
1282 wb->data608->font=FONT_REGULAR;
1283 if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb))
1285 if (debug_608)
1286 hb_log ("Two RU2, current line not empty. Simulating a CR\n");
1287 handle_command(0x14, 0x2D, wb);
1288 wb->rollup_cr = 1;
1290 wb->data608->current_visible_start_ms = get_last_pts(wb);
1291 wb->data608->mode=MODE_ROLLUP_2;
1292 erase_memory (wb, 0);
1293 wb->data608->cursor_column = 0;
1294 wb->data608->cursor_row = wb->data608->rollup_base_row;
1295 break;
1296 case COM_ROLLUP3:
1297 if (wb->data608->rollup_base_row + 1 < 3)
1299 move_roll_up(wb, 2);
1300 wb->data608->rollup_base_row = 2;
1302 if (wb->data608->mode==MODE_POPUP)
1304 if (write_cc_buffer(wb))
1305 wb->data608->screenfuls_counter++;
1306 erase_memory (wb, 1);
1308 wb->data608->color=default_color;
1309 wb->data608->font=FONT_REGULAR;
1310 if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb))
1312 if (debug_608)
1313 hb_log ("Two RU3, current line not empty. Simulating a CR\n");
1314 handle_command(0x14, 0x2D, wb);
1315 wb->rollup_cr = 1;
1317 wb->data608->current_visible_start_ms = get_last_pts(wb);
1318 wb->data608->mode=MODE_ROLLUP_3;
1319 erase_memory (wb, 0);
1320 wb->data608->cursor_column = 0;
1321 wb->data608->cursor_row = wb->data608->rollup_base_row;
1322 break;
1323 case COM_ROLLUP4:
1324 if (wb->data608->rollup_base_row + 1 < 4)
1326 move_roll_up(wb, 3);
1327 wb->data608->rollup_base_row = 3;
1329 if (wb->data608->mode==MODE_POPUP)
1331 if (write_cc_buffer(wb))
1332 wb->data608->screenfuls_counter++;
1333 erase_memory (wb, 1);
1335 wb->data608->color=default_color;
1336 wb->data608->font=FONT_REGULAR;
1337 if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb))
1339 if (debug_608)
1340 hb_log ("Two RU4, current line not empty. Simulating a CR\n");
1341 handle_command(0x14, 0x2D, wb);
1342 wb->rollup_cr = 1;
1344 wb->data608->current_visible_start_ms = get_last_pts(wb);
1345 wb->data608->mode = MODE_ROLLUP_4;
1346 wb->data608->cursor_column = 0;
1347 wb->data608->cursor_row = wb->data608->rollup_base_row;
1348 erase_memory (wb, 0);
1349 break;
1350 case COM_CARRIAGERETURN:
1351 // In transcript mode, CR doesn't write the whole screen, to avoid
1352 // repeated lines.
1354 // Skip initial CR if rollup has already done it
1355 if (wb->rollup_cr && is_current_row_empty(wb))
1357 wb->rollup_cr = 0;
1358 wb->data608->current_visible_start_ms = get_last_pts(wb);
1359 break;
1361 if (write_cc_buffer(wb))
1362 wb->data608->screenfuls_counter++;
1363 roll_up(wb);
1364 wb->data608->cursor_column = 0;
1365 wb->data608->current_visible_start_ms = get_last_pts(wb);
1366 break;
1367 case COM_ERASENONDISPLAYEDMEMORY:
1368 erase_memory (wb,0);
1369 break;
1370 case COM_ERASEDISPLAYEDMEMORY:
1371 // There may be "displayed" rollup data that has not been
1372 // written to a buffer yet.
1373 if (wb->data608->mode == MODE_ROLLUP_2 ||
1374 wb->data608->mode == MODE_ROLLUP_3 ||
1375 wb->data608->mode == MODE_ROLLUP_4)
1377 write_cc_buffer(wb);
1379 erase_memory (wb,1);
1381 // the last pts is the time to remove the previously
1382 // displayed CC from the display
1383 wb->data608->current_visible_start_ms = get_last_pts(wb);
1385 // Write "clear" subtitle if necessary
1386 struct eia608_screen *data;
1387 data = get_current_visible_buffer(wb);
1388 data->dirty = 1;
1389 write_cc_buffer(wb);
1390 break;
1391 case COM_ENDOFCAPTION: // Switch buffers
1392 // The currently *visible* buffer is leaving, so now we know it's ending
1393 // time. Time to actually write it to file.
1394 if (wb->data608->mode == MODE_POPUP)
1396 swap_visible_buffer(wb);
1397 wb->data608->current_visible_start_ms = get_last_pts(wb);
1399 if (write_cc_buffer(wb))
1400 wb->data608->screenfuls_counter++;
1402 if (wb->data608->mode != MODE_POPUP)
1403 swap_visible_buffer(wb);
1404 wb->data608->cursor_column = 0;
1405 wb->data608->cursor_row = 0;
1406 wb->data608->color=default_color;
1407 wb->data608->font=FONT_REGULAR;
1408 break;
1409 default:
1410 if (debug_608)
1412 hb_log ("\rNot yet implemented.\n");
1414 break;
1418 static void handle_end_of_data(struct s_write *wb)
1420 // We issue a EraseDisplayedMemory here so if there's any captions pending
1421 // they get written to file.
1422 handle_command (0x14, 0x2c, wb); // EDM
1425 static void handle_double(const unsigned char c1, const unsigned char c2,
1426 struct s_write *wb)
1428 unsigned char c;
1429 if (wb->data608->channel!=cc_channel)
1430 return;
1431 if (c2>=0x30 && c2<=0x3f)
1433 c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here
1434 if (debug_608)
1435 hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c);
1436 write_char(c,wb);
1440 /* Process EXTENDED CHARACTERS */
1441 static unsigned char handle_extended(unsigned char hi, unsigned char lo,
1442 struct s_write *wb)
1444 // Handle channel change
1445 if (wb->new_channel > 2)
1447 wb->new_channel -= 2;
1448 if (debug_608)
1449 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
1451 wb->data608->channel=wb->new_channel;
1452 if (wb->data608->channel!=cc_channel)
1453 return 0;
1455 // For lo values between 0x20-0x3f
1456 unsigned char c=0;
1458 if (debug_608)
1459 hb_log ("\rExtended: %02X %02X\n",hi,lo);
1460 if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13))
1462 switch (hi)
1464 case 0x12:
1465 c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here
1466 break;
1467 case 0x13:
1468 c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here
1469 break;
1471 // This column change is because extended characters replace
1472 // the previous character (which is sent for basic decoders
1473 // to show something similar to the real char)
1474 if (wb->data608->cursor_column>0)
1475 wb->data608->cursor_column--;
1477 write_char (c,wb);
1479 return 1;
1482 /* Process PREAMBLE ACCESS CODES (PAC) */
1483 static void handle_pac(unsigned char c1, unsigned char c2, struct s_write *wb)
1485 // Handle channel change
1486 if (wb->new_channel > 2)
1488 wb->new_channel -= 2;
1489 if (debug_608)
1490 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
1492 wb->data608->channel=wb->new_channel;
1493 if (wb->data608->channel!=cc_channel)
1494 return;
1496 int row=rowdata[((c1<<1)&14)|((c2>>5)&1)];
1498 if (debug_608)
1499 hb_log ("\rPAC: %02X %02X",c1,c2);
1501 if (c2>=0x40 && c2<=0x5f)
1503 c2=c2-0x40;
1505 else
1507 if (c2>=0x60 && c2<=0x7f)
1509 c2=c2-0x60;
1511 else
1513 if (debug_608)
1514 hb_log ("\rThis is not a PAC!!!!!\n");
1515 return;
1518 wb->data608->color=pac2_attribs[c2][0];
1519 wb->data608->font=pac2_attribs[c2][1];
1520 int indent=pac2_attribs[c2][2];
1521 if (debug_608)
1522 hb_log (" -- Position: %d:%d, color: %s, font: %s\n", row, indent,
1523 color_text[wb->data608->color][0],
1524 font_text[wb->data608->font]);
1526 // CC spec says to the preferred method to handle a roll-up base row
1527 // that causes the display to scroll off the top of the screen is to
1528 // adjust the base row down.
1529 int keep_lines;
1530 switch (wb->data608->mode)
1532 case MODE_ROLLUP_2:
1533 keep_lines = 2;
1534 break;
1535 case MODE_ROLLUP_3:
1536 keep_lines = 3;
1537 break;
1538 case MODE_ROLLUP_4:
1539 keep_lines = 4;
1540 break;
1541 default:
1542 // Not rollup mode, all rows ok
1543 keep_lines = 0;
1544 break;
1546 if (row < keep_lines)
1548 row = keep_lines;
1550 if (wb->data608->mode != MODE_TEXT)
1552 // According to Robson, row info is discarded in text mode
1553 // but column is accepted
1555 // CC-608 spec says current rollup display must move to the
1556 // new position when the cursor row changes
1557 move_roll_up(wb, row - 1);
1558 wb->data608->cursor_row = row - 1 ; // Since the array is 0 based
1560 wb->data608->rollup_base_row = row - 1;
1561 wb->data608->cursor_column = indent;
1565 static void handle_single(const unsigned char c1, struct s_write *wb)
1567 if (c1<0x20 || wb->data608->channel!=cc_channel)
1568 return; // We don't allow special stuff here
1569 if (debug_608)
1570 hb_log ("%c",c1);
1572 /*if (debug_608)
1573 hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */
1574 write_char (c1,wb);
1577 static int check_channel(unsigned char c1, struct s_write *wb)
1579 if (c1==0x14)
1581 if (debug_608 && wb->data608->channel!=1)
1582 hb_log ("\nChannel change, now 1\n");
1583 return 1;
1585 if (c1==0x1c)
1587 if (debug_608 && wb->data608->channel!=2)
1588 hb_log ("\nChannel change, now 2\n");
1589 return 2;
1591 if (c1==0x15)
1593 if (debug_608 && wb->data608->channel!=3)
1594 hb_log ("\nChannel change, now 3\n");
1595 return 3;
1597 if (c1==0x1d)
1599 if (debug_608 && wb->data608->channel!=4)
1600 hb_log ("\nChannel change, now 4\n");
1601 return 4;
1604 // Otherwise keep the current channel
1605 return wb->data608->channel;
1608 /* Handle Command, special char or attribute and also check for
1609 * channel changes.
1610 * Returns 1 if something was written to screen, 0 otherwise */
1611 static int disCommand(unsigned char hi, unsigned char lo, struct s_write *wb)
1613 int wrote_to_screen=0;
1615 /* Full channel changes are only allowed for "GLOBAL CODES",
1616 * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES",
1617 * "MID-ROW CODES".
1618 * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and
1619 * SPECIAL/SPECIAL CHARACTERS allow only switching
1620 * between 1&3 or 2&4. */
1621 wb->new_channel = check_channel (hi,wb);
1622 //if (wb->data608->channel!=cc_channel)
1623 // continue;
1625 if (hi>=0x18 && hi<=0x1f)
1626 hi=hi-8;
1628 switch (hi)
1630 case 0x10:
1631 if (lo>=0x40 && lo<=0x5f)
1632 handle_pac (hi,lo,wb);
1633 break;
1634 case 0x11:
1635 if (lo>=0x20 && lo<=0x2f)
1636 handle_text_attr (hi,lo,wb);
1637 if (lo>=0x30 && lo<=0x3f)
1639 wrote_to_screen=1;
1640 handle_double (hi,lo,wb);
1642 if (lo>=0x40 && lo<=0x7f)
1643 handle_pac (hi,lo,wb);
1644 break;
1645 case 0x12:
1646 case 0x13:
1647 if (lo>=0x20 && lo<=0x3f)
1649 wrote_to_screen=handle_extended (hi,lo,wb);
1651 if (lo>=0x40 && lo<=0x7f)
1652 handle_pac (hi,lo,wb);
1653 break;
1654 case 0x14:
1655 case 0x15:
1656 if (lo>=0x20 && lo<=0x2f)
1657 handle_command (hi,lo,wb);
1658 if (lo>=0x40 && lo<=0x7f)
1659 handle_pac (hi,lo,wb);
1660 break;
1661 case 0x16:
1662 if (lo>=0x40 && lo<=0x7f)
1663 handle_pac (hi,lo,wb);
1664 break;
1665 case 0x17:
1666 if (lo>=0x21 && lo<=0x23)
1667 handle_command (hi,lo,wb);
1668 if (lo>=0x2e && lo<=0x2f)
1669 handle_text_attr (hi,lo,wb);
1670 if (lo>=0x40 && lo<=0x7f)
1671 handle_pac (hi,lo,wb);
1672 break;
1674 return wrote_to_screen;
1677 static void process608(const unsigned char *data, int length,
1678 struct s_write *wb)
1680 static int textprinted = 0;
1681 int i;
1683 if (data!=NULL)
1685 for (i=0;i<length;i=i+2)
1687 unsigned char hi, lo;
1688 hi = data[i] & 0x7F; // Get rid of parity bit
1689 lo = data[i+1] & 0x7F; // Get rid of parity bit
1691 if (hi==0 && lo==0) // Just padding
1692 continue;
1693 if (hi>=0x01 && hi<=0x0E)
1695 // XDS crap - mode. Would be nice to support it eventually
1696 // wb->data608->last_c1=0;
1697 // wb->data608->last_c2=0;
1698 wb->data608->channel=3; // Always channel 3
1699 wb->in_xds_mode=1;
1701 if (hi==0x0F) // End of XDS block
1703 wb->in_xds_mode=0;
1704 continue;
1706 if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char
1707 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
1708 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
1710 // We were writing characters before, start a new line for
1711 // diagnostic output from disCommand()
1712 if (debug_608 && textprinted == 1 )
1714 hb_log("\n");
1715 textprinted = 0;
1718 wb->in_xds_mode=0; // Back to normal
1719 if (wb->data608->last_c1==hi && wb->data608->last_c2==lo)
1721 // Duplicate dual code, discard
1722 continue;
1724 wb->data608->last_c1=hi;
1725 wb->data608->last_c2=lo;
1726 disCommand (hi,lo,wb);
1728 if (hi>=0x20) // Standard characters (always in pairs)
1730 // Only print if the channel is active
1731 if (wb->data608->channel!=cc_channel)
1732 continue;
1734 if (debug_608)
1736 if( textprinted == 0 )
1738 hb_log("\n");
1739 textprinted = 1;
1743 handle_single(hi,wb);
1744 handle_single(lo,wb);
1745 wb->data608->last_c1=0;
1746 wb->data608->last_c2=0;
1749 if ( debug_608 && !textprinted && wb->data608->channel==cc_channel )
1750 { // Current FTS information after the characters are shown
1751 //hb_log("Current FTS: %s\n", print_mstime(get_last_pts()));
1754 if ((wb->data608->mode == MODE_ROLLUP_2 ||
1755 wb->data608->mode == MODE_ROLLUP_3 ||
1756 wb->data608->mode == MODE_ROLLUP_4) &&
1757 wb->direct_rollup)
1759 // If we are showing rollup on the fly (direct_rollup)
1760 // write a buffer now
1761 write_cc_buffer(wb);
1762 wb->data608->current_visible_start_ms = get_last_pts(wb);
1768 struct hb_work_private_s
1770 hb_job_t * job;
1771 struct s_write * cc608;
1774 static int decccInit( hb_work_object_t * w, hb_job_t * job )
1776 int retval = 1;
1777 hb_work_private_t * pv;
1779 pv = calloc( 1, sizeof( hb_work_private_t ) );
1780 if( pv )
1782 w->private_data = pv;
1784 pv->job = job;
1786 pv->cc608 = calloc(1, sizeof(struct s_write));
1788 if( pv->cc608 )
1790 pv->cc608->width = job->title->geometry.width;
1791 pv->cc608->height = job->title->geometry.height;
1792 memcpy(pv->cc608->crop, job->crop, sizeof(int[4]));
1793 pv->cc608->par = job->title->geometry.par;
1794 retval = general_608_init(pv->cc608);
1795 if( !retval )
1797 pv->cc608->data608 = calloc(1, sizeof(struct eia608));
1798 if( !pv->cc608->data608 )
1800 retval = 1;
1802 init_eia608(pv->cc608->data608);
1806 if (!retval)
1808 // Generate generic SSA Script Info.
1809 int height = job->title->geometry.height - job->crop[0] - job->crop[1];
1810 int width = job->title->geometry.width - job->crop[2] - job->crop[3];
1811 int safe_height = 0.8 * job->title->geometry.height;
1812 hb_subtitle_add_ssa_header(w->subtitle, "Courier New",
1813 .08 * safe_height, width, height);
1815 // When rendering subs, we need to push rollup subtitles out
1816 // asap (instead of waiting for a completed line) so that we
1817 // do not miss the frame that they should be rendered over.
1818 pv->cc608->direct_rollup = w->subtitle->config.dest == RENDERSUB;
1819 return retval;
1822 static int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
1823 hb_buffer_t ** buf_out )
1825 hb_work_private_t * pv = w->private_data;
1826 hb_buffer_t * in = *buf_in;
1828 if (in->s.flags & HB_BUF_FLAG_EOF)
1830 /* EOF on input stream - send it downstream & say that we're done */
1831 handle_end_of_data(pv->cc608);
1833 * Grab any pending buffer and output them with the EOF on the end
1835 if (pv->cc608->hb_last_buffer) {
1836 pv->cc608->hb_last_buffer->next = in;
1837 *buf_out = pv->cc608->hb_buffer;
1838 *buf_in = NULL;
1839 pv->cc608->hb_buffer = NULL;
1840 pv->cc608->hb_last_buffer = NULL;
1841 } else {
1842 *buf_out = in;
1843 *buf_in = NULL;
1845 return HB_WORK_DONE;
1848 pv->cc608->last_pts = in->s.start;
1849 process608(in->data, in->size, pv->cc608);
1852 * If there is one waiting then pass it on
1854 *buf_out = pv->cc608->hb_buffer;
1856 pv->cc608->hb_buffer = NULL;
1857 pv->cc608->hb_last_buffer = NULL;
1859 return HB_WORK_OK;
1862 static void decccClose( hb_work_object_t * w )
1864 hb_work_private_t * pv = w->private_data;
1865 general_608_close( pv->cc608 );
1866 free( pv->cc608->data608 );
1867 free( pv->cc608 );
1868 free( w->private_data );
1871 hb_work_object_t hb_deccc608 =
1873 WORK_DECCC608,
1874 "Closed Caption (608) decoder",
1875 decccInit,
1876 decccWork,
1877 decccClose