diff options
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/common.h | 5 | ||||
-rw-r--r-- | libhb/deccc608sub.c | 2403 | ||||
-rw-r--r-- | libhb/deccc608sub.h | 131 | ||||
-rw-r--r-- | libhb/decmpeg2.c | 239 | ||||
-rw-r--r-- | libhb/decvobsub.c (renamed from libhb/decsub.c) | 0 | ||||
-rw-r--r-- | libhb/muxcommon.c | 52 | ||||
-rw-r--r-- | libhb/muxmp4.c | 32 | ||||
-rw-r--r-- | libhb/scan.c | 1 | ||||
-rw-r--r-- | libhb/work.c | 7 |
9 files changed, 2844 insertions, 26 deletions
diff --git a/libhb/common.h b/libhb/common.h index f19511027..6a2c8d51d 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -634,7 +634,7 @@ struct hb_work_object_s hb_esconfig_t * config; /* Pointer hb_audio_t so we have access to the info in the audio worker threads. */ - hb_audio_t *audio; + hb_audio_t * audio; hb_work_private_t * private_data; @@ -642,9 +642,10 @@ struct hb_work_object_s volatile int * done; int status; int codec_param; + hb_title_t * title; hb_work_object_t * next; - int thread_sleep_interval; + int thread_sleep_interval; #endif }; diff --git a/libhb/deccc608sub.c b/libhb/deccc608sub.c new file mode 100644 index 000000000..2b0b199fa --- /dev/null +++ b/libhb/deccc608sub.c @@ -0,0 +1,2403 @@ +/* + * From ccextractor, leave this file as intact and close to the original as possible so that + * it is easy to patch in fixes - even though this file contains code that we don't need. + * + * Note that the SRT sub generation from CC could be useful for mkv subs. + */ +#include "hb.h" +#include "deccc608sub.h" + +/* + * ccextractor static configuration variables. + */ +static int debug_608 = 0; +static int trim_subs = 0; +static int nofontcolor = 0; +static enum encoding_type encoding = ENC_UTF_8; +static int cc_channel = 1; +static enum output_format write_format = OF_TRANSCRIPT; +static int sentence_cap = 1; +static int subs_delay = 0; +static LLONG screens_to_process = -1; +static int processed_enough = 0; +static int gui_mode_reports = 0; +static int norollup = 1; +static int direct_rollup = 0; + +static LLONG get_fts(void) +{ + return 0; +} + +#define fatal(N, ...) // N +#define XMLRPC_APPEND(N, ...) // N + +int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10}; +// Relationship between the first PAC byte and the row number + +// The following enc_buffer is not used at the moment, if it does get used +// we need to bring it into the swrite struct. Same for "str". +#define INITIAL_ENC_BUFFER_CAPACITY 2048 + +unsigned char *enc_buffer=NULL; // Generic general purpose buffer +unsigned char str[2048]; // Another generic general purpose buffer +unsigned enc_buffer_used; +unsigned enc_buffer_capacity; + +#define GUARANTEE(length) if (length>enc_buffer_capacity) \ +{enc_buffer_capacity*=2; enc_buffer=(unsigned char*) realloc (enc_buffer, enc_buffer_capacity); \ + if (enc_buffer==NULL) { fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory, bailing out\n"); } \ +} + +const unsigned char pac2_attribs[][3]= // Color, font, ident +{ + {COL_WHITE, FONT_REGULAR, 0}, // 0x40 || 0x60 + {COL_WHITE, FONT_UNDERLINED, 0}, // 0x41 || 0x61 + {COL_GREEN, FONT_REGULAR, 0}, // 0x42 || 0x62 + {COL_GREEN, FONT_UNDERLINED, 0}, // 0x43 || 0x63 + {COL_BLUE, FONT_REGULAR, 0}, // 0x44 || 0x64 + {COL_BLUE, FONT_UNDERLINED, 0}, // 0x45 || 0x65 + {COL_CYAN, FONT_REGULAR, 0}, // 0x46 || 0x66 + {COL_CYAN, FONT_UNDERLINED, 0}, // 0x47 || 0x67 + {COL_RED, FONT_REGULAR, 0}, // 0x48 || 0x68 + {COL_RED, FONT_UNDERLINED, 0}, // 0x49 || 0x69 + {COL_YELLOW, FONT_REGULAR, 0}, // 0x4a || 0x6a + {COL_YELLOW, FONT_UNDERLINED, 0}, // 0x4b || 0x6b + {COL_MAGENTA, FONT_REGULAR, 0}, // 0x4c || 0x6c + {COL_MAGENTA, FONT_UNDERLINED, 0}, // 0x4d || 0x6d + {COL_WHITE, FONT_ITALICS, 0}, // 0x4e || 0x6e + {COL_WHITE, FONT_UNDERLINED_ITALICS, 0}, // 0x4f || 0x6f + {COL_WHITE, FONT_REGULAR, 0}, // 0x50 || 0x70 + {COL_WHITE, FONT_UNDERLINED, 0}, // 0x51 || 0x71 + {COL_WHITE, FONT_REGULAR, 4}, // 0x52 || 0x72 + {COL_WHITE, FONT_UNDERLINED, 4}, // 0x53 || 0x73 + {COL_WHITE, FONT_REGULAR, 8}, // 0x54 || 0x74 + {COL_WHITE, FONT_UNDERLINED, 8}, // 0x55 || 0x75 + {COL_WHITE, FONT_REGULAR, 12}, // 0x56 || 0x76 + {COL_WHITE, FONT_UNDERLINED, 12}, // 0x57 || 0x77 + {COL_WHITE, FONT_REGULAR, 16}, // 0x58 || 0x78 + {COL_WHITE, FONT_UNDERLINED, 16}, // 0x59 || 0x79 + {COL_WHITE, FONT_REGULAR, 20}, // 0x5a || 0x7a + {COL_WHITE, FONT_UNDERLINED, 20}, // 0x5b || 0x7b + {COL_WHITE, FONT_REGULAR, 24}, // 0x5c || 0x7c + {COL_WHITE, FONT_UNDERLINED, 24}, // 0x5d || 0x7d + {COL_WHITE, FONT_REGULAR, 28}, // 0x5e || 0x7e + {COL_WHITE, FONT_UNDERLINED, 28} // 0x5f || 0x7f +}; + +// Preencoded strings +unsigned char encoded_crlf[16]; +unsigned int encoded_crlf_length; +unsigned char encoded_br[16]; +unsigned int encoded_br_length; + +// Default color +unsigned char usercolor_rgb[8]=""; +enum color_code default_color=COL_WHITE; + +const char *sami_header= // TODO: Revise the <!-- comments +"<SAMI>\n\ +<HEAD>\n\ +<STYLE TYPE=\"text/css\">\n\ +<!--\n\ +P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n\ +text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n\ +.UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n\ +-->\n\ +</STYLE>\n\ +</HEAD>\n\n\ +<BODY>\n"; + +const char *command_type[] = +{ + "Unknown", + "EDM - EraseDisplayedMemory", + "RCL - ResumeCaptionLoading", + "EOC - End Of Caption", + "TO1 - Tab Offset, 1 column", + "TO2 - Tab Offset, 2 column", + "TO3 - Tab Offset, 3 column", + "RU2 - Roll up 2 rows", + "RU3 - Roll up 3 rows", + "RU4 - Roll up 4 rows", + "CR - Carriage Return", + "ENM - Erase non-displayed memory", + "BS - Backspace", + "RTD - Resume Text Display" +}; + +const char *font_text[]= +{ + "regular", + "italics", + "underlined", + "underlined italics" +}; + +const char *cc_modes_text[]= +{ + "Pop-Up captions" +}; + +const char *color_text[][2]= +{ + {"white",""}, + {"green","<font color=\"#00ff00\">"}, + {"blue","<font color=\"#0000ff\">"}, + {"cyan","<font color=\"#00ffff\">"}, + {"red","<font color=\"#ff0000\">"}, + {"yellow","<font color=\"#ffff00\">"}, + {"magenta","<font color=\"#ff00ff\">"}, + {"userdefined","<font color=\""} +}; + +int general_608_init (struct s_write *wb) +{ + /* + * Not currently used. + * + if( !enc_buffer ) + { + enc_buffer=(unsigned char *) malloc (INITIAL_ENC_BUFFER_CAPACITY); + if (enc_buffer==NULL) + return -1; + enc_buffer_capacity=INITIAL_ENC_BUFFER_CAPACITY; + } + */ + + if( !wb->subline) { + wb->subline = malloc(2048); + + if (!wb->subline) + { + return -1; + } + } + + wb->new_sentence = 1; + wb->new_channel = 1; + wb->in_xds_mode = 0; + return 0; +} + +/* + * Free up CC memory - don't call this from HB just yet since it will cause + * parallel encodes to fail - to be honest they will be stuffed anyway since + * the CC's may be overwriting the buffers. + */ +void general_608_close (struct s_write *wb) +{ + if( enc_buffer ) { + free(enc_buffer); + enc_buffer_capacity = 0; + enc_buffer_used = 0; + } + if( wb->subline ) { + free(wb->subline); + } +} + + +#include <ctype.h> + +void get_char_in_latin_1 (unsigned char *buffer, unsigned char c) +{ + unsigned char c1='?'; + if (c<0x80) + { + // Regular line-21 character set, mostly ASCII except these exceptions + switch (c) + { + case 0x2a: // lowercase a, acute accent + c1=0xe1; + break; + case 0x5c: // lowercase e, acute accent + c1=0xe9; + break; + case 0x5e: // lowercase i, acute accent + c1=0xed; + break; + case 0x5f: // lowercase o, acute accent + c1=0xf3; + break; + case 0x60: // lowercase u, acute accent + c1=0xfa; + break; + case 0x7b: // lowercase c with cedilla + c1=0xe7; + break; + case 0x7c: // division symbol + c1=0xf7; + break; + case 0x7d: // uppercase N tilde + c1=0xd1; + break; + case 0x7e: // lowercase n tilde + c1=0xf1; + break; + default: + c1=c; + break; + } + *buffer=c1; + return; + } + switch (c) + { + // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F + case 0x80: // Registered symbol (R) + c1=0xae; + break; + case 0x81: // degree sign + c1=0xb0; + break; + case 0x82: // 1/2 symbol + c1=0xbd; + break; + case 0x83: // Inverted (open) question mark + c1=0xbf; + break; + case 0x84: // Trademark symbol (TM) - Does not exist in Latin 1 + break; + case 0x85: // Cents symbol + c1=0xa2; + break; + case 0x86: // Pounds sterling + c1=0xa3; + break; + case 0x87: // Music note - Not in latin 1, so we use 'pilcrow' + c1=0xb6; + break; + case 0x88: // lowercase a, grave accent + c1=0xe0; + break; + case 0x89: // transparent space, we make it regular + c1=0x20; + break; + case 0x8a: // lowercase e, grave accent + c1=0xe8; + break; + case 0x8b: // lowercase a, circumflex accent + c1=0xe2; + break; + case 0x8c: // lowercase e, circumflex accent + c1=0xea; + break; + case 0x8d: // lowercase i, circumflex accent + c1=0xee; + break; + case 0x8e: // lowercase o, circumflex accent + c1=0xf4; + break; + case 0x8f: // lowercase u, circumflex accent + c1=0xfb; + break; + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F + case 0x90: // capital letter A with acute + c1=0xc1; + break; + case 0x91: // capital letter E with acute + c1=0xc9; + break; + case 0x92: // capital letter O with acute + c1=0xd3; + break; + case 0x93: // capital letter U with acute + c1=0xda; + break; + case 0x94: // capital letter U with diaresis + c1=0xdc; + break; + case 0x95: // lowercase letter U with diaeresis + c1=0xfc; + break; + case 0x96: // apostrophe + c1=0x27; + break; + case 0x97: // inverted exclamation mark + c1=0xa1; + break; + case 0x98: // asterisk + c1=0x2a; + break; + case 0x99: // apostrophe (yes, duped). See CCADI source code. + c1=0x27; + break; + case 0x9a: // hyphen-minus + c1=0x2d; + break; + case 0x9b: // copyright sign + c1=0xa9; + break; + case 0x9c: // Service Mark - not available in latin 1 + break; + case 0x9d: // Full stop (.) + c1=0x2e; + break; + case 0x9e: // Quoatation mark + c1=0x22; + break; + case 0x9f: // Quoatation mark + c1=0x22; + break; + case 0xa0: // uppercase A, grave accent + c1=0xc0; + break; + case 0xa1: // uppercase A, circumflex + c1=0xc2; + break; + case 0xa2: // uppercase C with cedilla + c1=0xc7; + break; + case 0xa3: // uppercase E, grave accent + c1=0xc8; + break; + case 0xa4: // uppercase E, circumflex + c1=0xca; + break; + case 0xa5: // capital letter E with diaresis + c1=0xcb; + break; + case 0xa6: // lowercase letter e with diaresis + c1=0xeb; + break; + case 0xa7: // uppercase I, circumflex + c1=0xce; + break; + case 0xa8: // uppercase I, with diaresis + c1=0xcf; + break; + case 0xa9: // lowercase i, with diaresis + c1=0xef; + break; + case 0xaa: // uppercase O, circumflex + c1=0xd4; + break; + case 0xab: // uppercase U, grave accent + c1=0xd9; + break; + case 0xac: // lowercase u, grave accent + c1=0xf9; + break; + case 0xad: // uppercase U, circumflex + c1=0xdb; + break; + case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + c1=0xab; + break; + case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + c1=0xbb; + break; + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F + case 0xb0: // Uppercase A, tilde + c1=0xc3; + break; + case 0xb1: // Lowercase a, tilde + c1=0xe3; + break; + case 0xb2: // Uppercase I, acute accent + c1=0xcd; + break; + case 0xb3: // Uppercase I, grave accent + c1=0xcc; + break; + case 0xb4: // Lowercase i, grave accent + c1=0xec; + break; + case 0xb5: // Uppercase O, grave accent + c1=0xd2; + break; + case 0xb6: // Lowercase o, grave accent + c1=0xf2; + break; + case 0xb7: // Uppercase O, tilde + c1=0xd5; + break; + case 0xb8: // Lowercase o, tilde + c1=0xf5; + break; + case 0xb9: // Open curly brace + c1=0x7b; + break; + case 0xba: // Closing curly brace + c1=0x7d; + break; + case 0xbb: // Backslash + c1=0x5c; + break; + case 0xbc: // Caret + c1=0x5e; + break; + case 0xbd: // Underscore + c1=0x5f; + break; + case 0xbe: // Pipe (broken bar) + c1=0xa6; + break; + case 0xbf: // Tilde + c1=0x7e; + break; + case 0xc0: // Uppercase A, umlaut + c1=0xc4; + break; + case 0xc1: // Lowercase A, umlaut + c1=0xe3; + break; + case 0xc2: // Uppercase O, umlaut + c1=0xd6; + break; + case 0xc3: // Lowercase o, umlaut + c1=0xf6; + break; + case 0xc4: // Esszett (sharp S) + c1=0xdf; + break; + case 0xc5: // Yen symbol + c1=0xa5; + break; + case 0xc6: // Currency symbol + c1=0xa4; + break; + case 0xc7: // Vertical bar + c1=0x7c; + break; + case 0xc8: // Uppercase A, ring + c1=0xc5; + break; + case 0xc9: // Lowercase A, ring + c1=0xe5; + break; + case 0xca: // Uppercase O, slash + c1=0xd8; + break; + case 0xcb: // Lowercase o, slash + c1=0xf8; + break; + case 0xcc: // Upper left corner + case 0xcd: // Upper right corner + case 0xce: // Lower left corner + case 0xcf: // Lower right corner + default: // For those that don't have representation + *buffer='?'; // I'll do it eventually, I promise + break; // This are weird chars anyway + } + *buffer=c1; +} + +void get_char_in_unicode (unsigned char *buffer, unsigned char c) +{ + unsigned char c1,c2; + switch (c) + { + case 0x84: // Trademark symbol (TM) + c2=0x21; + c1=0x22; + break; + case 0x87: // Music note + c2=0x26; + c1=0x6a; + break; + case 0x9c: // Service Mark + c2=0x21; + c1=0x20; + break; + case 0xcc: // Upper left corner + c2=0x23; + c1=0x1c; + break; + case 0xcd: // Upper right corner + c2=0x23; + c1=0x1d; + break; + case 0xce: // Lower left corner + c2=0x23; + c1=0x1e; + break; + case 0xcf: // Lower right corner + c2=0x23; + c1=0x1f; + break; + default: // Everything else, same as latin-1 followed by 00 + get_char_in_latin_1 (&c1,c); + c2=0; + break; + } + *buffer=c1; + *(buffer+1)=c2; +} + +int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number of bytes used +{ + if (c<0x80) // Regular line-21 character set, mostly ASCII except these exceptions + { + switch (c) + { + case 0x2a: // lowercase a, acute accent + *buffer=0xc3; + *(buffer+1)=0xa1; + return 2; + case 0x5c: // lowercase e, acute accent + *buffer=0xc3; + *(buffer+1)=0xa9; + return 2; + case 0x5e: // lowercase i, acute accent + *buffer=0xc3; + *(buffer+1)=0xad; + return 2; + case 0x5f: // lowercase o, acute accent + *buffer=0xc3; + *(buffer+1)=0xb3; + return 2; + case 0x60: // lowercase u, acute accent + *buffer=0xc3; + *(buffer+1)=0xba; + return 2; + case 0x7b: // lowercase c with cedilla + *buffer=0xc3; + *(buffer+1)=0xa7; + return 2; + case 0x7c: // division symbol + *buffer=0xc3; + *(buffer+1)=0xb7; + return 2; + case 0x7d: // uppercase N tilde + *buffer=0xc3; + *(buffer+1)=0x91; + return 2; + case 0x7e: // lowercase n tilde + *buffer=0xc3; + *(buffer+1)=0xb1; + return 2; + default: + *buffer=c; + return 1; + } + } + switch (c) + { + // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F + case 0x80: // Registered symbol (R) + *buffer=0xc2; + *(buffer+1)=0xae; + return 2; + case 0x81: // degree sign + *buffer=0xc2; + *(buffer+1)=0xb0; + return 2; + case 0x82: // 1/2 symbol + *buffer=0xc2; + *(buffer+1)=0xbd; + return 2; + case 0x83: // Inverted (open) question mark + *buffer=0xc2; + *(buffer+1)=0xbf; + return 2; + case 0x84: // Trademark symbol (TM) + *buffer=0xe2; + *(buffer+1)=0x84; + *(buffer+2)=0xa2; + return 3; + case 0x85: // Cents symbol + *buffer=0xc2; + *(buffer+1)=0xa2; + return 2; + case 0x86: // Pounds sterling + *buffer=0xc2; + *(buffer+1)=0xa3; + return 2; + case 0x87: // Music note + *buffer=0xe2; + *(buffer+1)=0x99; + *(buffer+2)=0xaa; + return 3; + case 0x88: // lowercase a, grave accent + *buffer=0xc3; + *(buffer+1)=0xa0; + return 2; + case 0x89: // transparent space, we make it regular + *buffer=0x20; + return 1; + case 0x8a: // lowercase e, grave accent + *buffer=0xc3; + *(buffer+1)=0xa8; + return 2; + case 0x8b: // lowercase a, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xa2; + return 2; + case 0x8c: // lowercase e, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xaa; + return 2; + case 0x8d: // lowercase i, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xae; + return 2; + case 0x8e: // lowercase o, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xb4; + return 2; + case 0x8f: // lowercase u, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xbb; + return 2; + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F + case 0x90: // capital letter A with acute + *buffer=0xc3; + *(buffer+1)=0x81; + return 2; + case 0x91: // capital letter E with acute + *buffer=0xc3; + *(buffer+1)=0x89; + return 2; + case 0x92: // capital letter O with acute + *buffer=0xc3; + *(buffer+1)=0x93; + return 2; + case 0x93: // capital letter U with acute + *buffer=0xc3; + *(buffer+1)=0x9a; + return 2; + case 0x94: // capital letter U with diaresis + *buffer=0xc3; + *(buffer+1)=0x9c; + return 2; + case 0x95: // lowercase letter U with diaeresis + *buffer=0xc3; + *(buffer+1)=0xbc; + return 2; + case 0x96: // apostrophe + *buffer=0x27; + return 1; + case 0x97: // inverted exclamation mark + *buffer=0xc1; + *(buffer+1)=0xa1; + return 2; + case 0x98: // asterisk + *buffer=0x2a; + return 1; + case 0x99: // apostrophe (yes, duped). See CCADI source code. + *buffer=0x27; + return 1; + case 0x9a: // hyphen-minus + *buffer=0x2d; + return 1; + case 0x9b: // copyright sign + *buffer=0xc2; + *(buffer+1)=0xa9; + return 2; + case 0x9c: // Service mark + *buffer=0xe2; + *(buffer+1)=0x84; + *(buffer+2)=0xa0; + return 3; + case 0x9d: // Full stop (.) + *buffer=0x2e; + return 1; + case 0x9e: // Quoatation mark + *buffer=0x22; + return 1; + case 0x9f: // Quoatation mark + *buffer=0x22; + return 1; + case 0xa0: // uppercase A, grave accent + *buffer=0xc3; + *(buffer+1)=0x80; + return 2; + case 0xa1: // uppercase A, circumflex + *buffer=0xc3; + *(buffer+1)=0x82; + return 2; + case 0xa2: // uppercase C with cedilla + *buffer=0xc3; + *(buffer+1)=0x87; + return 2; + case 0xa3: // uppercase E, grave accent + *buffer=0xc3; + *(buffer+1)=0x88; + return 2; + case 0xa4: // uppercase E, circumflex + *buffer=0xc3; + *(buffer+1)=0x8a; + return 2; + case 0xa5: // capital letter E with diaresis + *buffer=0xc3; + *(buffer+1)=0x8b; + return 2; + case 0xa6: // lowercase letter e with diaresis + *buffer=0xc3; + *(buffer+1)=0xab; + return 2; + case 0xa7: // uppercase I, circumflex + *buffer=0xc3; + *(buffer+1)=0x8e; + return 2; + case 0xa8: // uppercase I, with diaresis + *buffer=0xc3; + *(buffer+1)=0x8f; + return 2; + case 0xa9: // lowercase i, with diaresis + *buffer=0xc3; + *(buffer+1)=0xaf; + return 2; + case 0xaa: // uppercase O, circumflex + *buffer=0xc3; + *(buffer+1)=0x94; + return 2; + case 0xab: // uppercase U, grave accent + *buffer=0xc3; + *(buffer+1)=0x99; + return 2; + case 0xac: // lowercase u, grave accent + *buffer=0xc3; + *(buffer+1)=0xb9; + return 2; + case 0xad: // uppercase U, circumflex + *buffer=0xc3; + *(buffer+1)=0x9b; + return 2; + case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + *buffer=0xc2; + *(buffer+1)=0xab; + return 2; + case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + *buffer=0xc2; + *(buffer+1)=0xbb; + return 2; + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F + case 0xb0: // Uppercase A, tilde + *buffer=0xc3; + *(buffer+1)=0x83; + return 2; + case 0xb1: // Lowercase a, tilde + *buffer=0xc3; + *(buffer+1)=0xa3; + return 2; + case 0xb2: // Uppercase I, acute accent + *buffer=0xc3; + *(buffer+1)=0x8d; + return 2; + case 0xb3: // Uppercase I, grave accent + *buffer=0xc3; + *(buffer+1)=0x8c; + return 2; + case 0xb4: // Lowercase i, grave accent + *buffer=0xc3; + *(buffer+1)=0xac; + return 2; + case 0xb5: // Uppercase O, grave accent + *buffer=0xc3; + *(buffer+1)=0x92; + return 2; + case 0xb6: // Lowercase o, grave accent + *buffer=0xc3; + *(buffer+1)=0xb2; + return 2; + case 0xb7: // Uppercase O, tilde + *buffer=0xc3; + *(buffer+1)=0x95; + return 2; + case 0xb8: // Lowercase o, tilde + *buffer=0xc3; + *(buffer+1)=0xb5; + return 2; + case 0xb9: // Open curly brace + *buffer=0x7b; + return 1; + case 0xba: // Closing curly brace + *buffer=0x7d; + return 1; + case 0xbb: // Backslash + *buffer=0x5c; + return 1; + case 0xbc: // Caret + *buffer=0x5e; + return 1; + case 0xbd: // Underscore + *buffer=0x5f; + return 1; + case 0xbe: // Pipe (broken bar) + *buffer=0xc2; + *(buffer+1)=0xa6; + return 1; + case 0xbf: // Tilde + *buffer=0x7e; // Not sure + return 1; + case 0xc0: // Uppercase A, umlaut + *buffer=0xc3; + *(buffer+1)=0x84; + return 2; + case 0xc1: // Lowercase A, umlaut + *buffer=0xc3; + *(buffer+1)=0xa4; + return 2; + case 0xc2: // Uppercase O, umlaut + *buffer=0xc3; + *(buffer+1)=0x96; + return 2; + case 0xc3: // Lowercase o, umlaut + *buffer=0xc3; + *(buffer+1)=0xb6; + return 2; + case 0xc4: // Esszett (sharp S) + *buffer=0xc3; + *(buffer+1)=0x9f; + return 2; + case 0xc5: // Yen symbol + *buffer=0xc2; + *(buffer+1)=0xa5; + return 2; + case 0xc6: // Currency symbol + *buffer=0xc2; + *(buffer+1)=0xa4; + return 2; + case 0xc7: // Vertical bar + *buffer=0x7c; + return 1; + case 0xc8: // Uppercase A, ring + *buffer=0xc3; + *(buffer+1)=0x85; + return 2; + case 0xc9: // Lowercase A, ring + *buffer=0xc3; + *(buffer+1)=0xa5; + return 2; + case 0xca: // Uppercase O, slash + *buffer=0xc3; + *(buffer+1)=0x98; + return 2; + case 0xcb: // Lowercase o, slash + *buffer=0xc3; + *(buffer+1)=0xb8; + return 2; + case 0xcc: // Upper left corner + *buffer=0xe2; + *(buffer+1)=0x8c; + *(buffer+2)=0x9c; + return 3; + case 0xcd: // Upper right corner + *buffer=0xe2; + *(buffer+1)=0x8c; + *(buffer+2)=0x9d; + return 3; + case 0xce: // Lower left corner + *buffer=0xe2; + *(buffer+1)=0x8c; + *(buffer+2)=0x9e; + return 3; + case 0xcf: // Lower right corner + *buffer=0xe2; + *(buffer+1)=0x8c; + *(buffer+2)=0x9f; + return 3; + default: // + *buffer='?'; // I'll do it eventually, I promise + return 1; // This are weird chars anyway + } +} + +unsigned char cctolower (unsigned char c) +{ + if (c>='A' && c<='Z') + return tolower(c); + switch (c) + { + case 0x7d: // uppercase N tilde + return 0x7e; + case 0x90: // capital letter A with acute + return 0x2a; + case 0x91: // capital letter E with acute + return 0x5c; + case 0x92: // capital letter O with acute + return 0x5f; + case 0x93: // capital letter U with acute + return 0x60; + case 0xa2: // uppercase C with cedilla + return 0x7b; + case 0xa0: // uppercase A, grave accent + return 0x88; + case 0xa3: // uppercase E, grave accent + return 0x8a; + case 0xa1: // uppercase A, circumflex + return 0x8b; + case 0xa4: // uppercase E, circumflex + return 0x8c; + case 0xa7: // uppercase I, circumflex + return 0x8d; + case 0xaa: // uppercase O, circumflex + return 0x8e; + case 0xad: // uppercase U, circumflex + return 0x8f; + case 0x94: // capital letter U with diaresis + return 0x95; + case 0xa5: // capital letter E with diaresis + return 0xa6; + case 0xa8: // uppercase I, with diaresis + return 0xa9; + case 0xab: // uppercase U, grave accent + return 0xac; + case 0xb0: // Uppercase A, tilde + return 0xb1; + case 0xb2: // Uppercase I, acute accent + return 0x5e; + case 0xb3: // Uppercase I, grave accent + return 0xb4; + case 0xb5: // Uppercase O, grave accent + return 0xb6; + case 0xb7: // Uppercase O, tilde + return 0xb8; + case 0xc0: // Uppercase A, umlaut + return 0xc1; + case 0xc2: // Uppercase O, umlaut + return 0xc3; + case 0xc8: // Uppercase A, ring + return 0xc9; + case 0xca: // Uppercase O, slash + return 0xcb; + } + return c; +} + +unsigned char cctoupper (unsigned char c) +{ + if (c>='a' && c<='z') + return toupper(c); + switch (c) + { + case 0x7e: // lowercase n tilde + return 0x7d; + case 0x2a: // lowercase a, acute accent + return 0x90; + case 0x5c: // lowercase e, acute accent + return 0x91; + case 0x5e: // lowercase i, acute accent + return 0xb2; + case 0x5f: // lowercase o, acute accent + return 0x92; + case 0x60: // lowercase u, acute accent + return 0x93; + case 0x7b: // lowercase c with cedilla + return 0xa2; + case 0x88: // lowercase a, grave accent + return 0xa0; + case 0x8a: // lowercase e, grave accent + return 0xa3; + case 0x8b: // lowercase a, circumflex accent + return 0xa1; + case 0x8c: // lowercase e, circumflex accent + return 0xa4; + case 0x8d: // lowercase i, circumflex accent + return 0xa7; + case 0x8e: // lowercase o, circumflex accent + return 0xaa; + case 0x8f: // lowercase u, circumflex accent + return 0xad; + case 0x95: // lowercase letter U with diaeresis + return 0x94; + case 0xa6: // lowercase letter e with diaresis + return 0xa5; + case 0xa9: // lowercase i, with diaresis + return 0xa8; + case 0xac: // lowercase u, grave accent + return 0xab; + case 0xb1: // Lowercase a, tilde + return 0xb0; + case 0xb4: // Lowercase i, grave accent + return 0xb3; + case 0xb6: // Lowercase o, grave accent + return 0xb5; + case 0xb8: // Lowercase o, tilde + return 0xb7; + case 0xc1: // Lowercase A, umlaut + return 0xc0; + case 0xc3: // Lowercase o, umlaut + return 0xc2; + case 0xc9: // Lowercase A, ring + return 0xc8; + case 0xcb: // Lowercase o, slash + return 0xca; + } + return c; +} + + +// Encodes a generic string. Note that since we use the encoders for closed caption +// data, text would have to be encoded as CCs... so using special characters here +// it's a bad idea. +unsigned encode_line (unsigned char *buffer, unsigned char *text) +{ + unsigned bytes=0; + while (*text) + { + switch (encoding) + { + case ENC_UTF_8: + case ENC_LATIN_1: + *buffer=*text; + bytes++; + buffer++; + break; + case ENC_UNICODE: + *buffer=*text; + *(buffer+1)=0; + bytes+=2; + buffer+=2; + break; + } + text++; + } + return bytes; +} + +#define ISSEPARATOR(c) (c==' ' || c==0x89 || ispunct(c) \ + || c==0x99) // This is the apostrofe. We get it here in CC encoding, not ASCII + + +void correct_case (int line_num, struct eia608_screen *data) +{ +/* int i=0; */ +/* while (i<spell_words) */ +/* { */ +/* char *c=(char *) data->characters[line_num]; */ +/* size_t len=strlen (spell_correct[i]); */ +/* while ((c=strstr (c,spell_lower[i]))!=NULL) */ +/* { */ +/* // Make sure it's a whole word (start of line or */ +/* // preceded by space, and end of line or followed by */ +/* // space) */ +/* unsigned char prev; */ +/* if (c==(char *) data->characters[line_num]) // Beginning of line... */ +/* prev=' '; // ...Pretend we had a blank before */ +/* else */ +/* prev=*(c-1); */ +/* unsigned char next; */ +/* if (c-(char *) data->characters[line_num]+len==CC608_SCREEN_WIDTH) // End of line... */ +/* next=' '; // ... pretend we have a blank later */ +/* else */ +/* next=*(c+len); */ +/* if ( ISSEPARATOR(prev) && ISSEPARATOR(next)) */ +/* { */ +/* memcpy (c,spell_correct[i],len); */ +/* } */ +/* c++; */ +/* } */ +/* i++; */ +/* } */ +} + +void capitalize (int line_num, struct eia608_screen *data, int *new_sentence) +{ + int i; + + for (i=0;i<CC608_SCREEN_WIDTH;i++) + { + switch (data->characters[line_num][i]) + { + case ' ': + case 0x89: // This is a transparent space + case '-': + break; + case '.': // Fallthrough + case '?': // Fallthrough + case '!': + case ':': + *new_sentence=1; + break; + default: + if (*new_sentence) + data->characters[line_num][i]=cctoupper (data->characters[line_num][i]); + else + data->characters[line_num][i]=cctolower (data->characters[line_num][i]); + *new_sentence=0; + break; + } + } +} + +void find_limit_characters (unsigned char *line, int *first_non_blank, int *last_non_blank) +{ + int i; + + *last_non_blank=-1; + *first_non_blank=-1; + for (i=0;i<CC608_SCREEN_WIDTH;i++) + { + unsigned char c=line[i]; + if (c!=' ' && c!=0x89) + { + if (*first_non_blank==-1) + *first_non_blank=i; + *last_non_blank=i; + } + } +} + +unsigned get_decoder_line_basic (unsigned char *buffer, int line_num, struct eia608_screen *data) +{ + unsigned char *line = data->characters[line_num]; + int last_non_blank=-1; + int first_non_blank=-1; + unsigned char *orig=buffer; // Keep for debugging + int i; + find_limit_characters (line, &first_non_blank, &last_non_blank); + + if (first_non_blank==-1) + { + *buffer=0; + return 0; + } + + int bytes=0; + for (i=first_non_blank;i<=last_non_blank;i++) + { + char c=line[i]; + switch (encoding) + { + case ENC_UTF_8: + bytes=get_char_in_utf_8 (buffer,c); + break; + case ENC_LATIN_1: + get_char_in_latin_1 (buffer,c); + bytes=1; + break; + case ENC_UNICODE: + get_char_in_unicode (buffer,c); + bytes=2; + break; + } + buffer+=bytes; + } + *buffer=0; + return (unsigned) (buffer-orig); // Return length +} + +unsigned get_decoder_line_encoded_for_gui (unsigned char *buffer, int line_num, struct eia608_screen *data) +{ + unsigned char *line = data->characters[line_num]; + unsigned char *orig=buffer; // Keep for debugging + int first=0, last=31; + int i; + + find_limit_characters(line,&first,&last); + for (i=first;i<=last;i++) + { + get_char_in_latin_1 (buffer,line[i]); + buffer++; + } + *buffer=0; + return (unsigned) (buffer-orig); // Return length + +} + +unsigned get_decoder_line_encoded (unsigned char *buffer, int line_num, struct eia608_screen *data) +{ + int col = COL_WHITE; + int underlined = 0; + int italics = 0; + int i; + + unsigned char *line = data->characters[line_num]; + unsigned char *orig=buffer; // Keep for debugging + int first=0, last=31; + if (trim_subs) + find_limit_characters(line,&first,&last); + for (i=first;i<=last;i++) + { + // Handle color + int its_col = data->colors[line_num][i]; + if (its_col != col && !nofontcolor) + { + if (col!=COL_WHITE) // We need to close the previous font tag + { + buffer+= encode_line (buffer,(unsigned char *) "</font>"); + } + // Add new font tag + buffer+=encode_line (buffer, (unsigned char*) color_text[its_col][1]); + if (its_col==COL_USERDEFINED) + { + // The previous sentence doesn't copy the whole + // <font> tag, just up to the quote before the color + buffer+=encode_line (buffer, (unsigned char*) usercolor_rgb); + buffer+=encode_line (buffer, (unsigned char*) "\">"); + } + + col = its_col; + } + // Handle underlined + int is_underlined = data->fonts[line_num][i] & FONT_UNDERLINED; + if (is_underlined && underlined==0) // Open underline + { + buffer+=encode_line (buffer, (unsigned char *) "<u>"); + } + if (is_underlined==0 && underlined) // Close underline + { + buffer+=encode_line (buffer, (unsigned char *) "</u>"); + } + underlined=is_underlined; + // Handle italics + int has_ita = data->fonts[line_num][i] & FONT_ITALICS; + if (has_ita && italics==0) // Open italics + { + buffer+=encode_line (buffer, (unsigned char *) "<i>"); + } + if (has_ita==0 && italics) // Close italics + { + buffer+=encode_line (buffer, (unsigned char *) "</i>"); + } + italics=has_ita; + int bytes=0; + switch (encoding) + { + case ENC_UTF_8: + bytes=get_char_in_utf_8 (buffer,line[i]); + break; + case ENC_LATIN_1: + get_char_in_latin_1 (buffer,line[i]); + bytes=1; + break; + case ENC_UNICODE: + get_char_in_unicode (buffer,line[i]); + bytes=2; + break; + } + buffer+=bytes; + } + if (italics) + { + buffer+=encode_line (buffer, (unsigned char *) "</i>"); + } + if (underlined) + { + buffer+=encode_line (buffer, (unsigned char *) "</u>"); + } + if (col != COL_WHITE && !nofontcolor) + { + buffer+=encode_line (buffer, (unsigned char *) "</font>"); + } + *buffer=0; + return (unsigned) (buffer-orig); // Return length +} + + +void delete_all_lines_but_current (struct eia608_screen *data, int row) +{ + int i; + for (i=0;i<15;i++) + { + if (i!=row) + { + memset(data->characters[i],' ',CC608_SCREEN_WIDTH); + data->characters[i][CC608_SCREEN_WIDTH]=0; + memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1); + memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1); + data->row_used[i]=0; + } + } +} + +void clear_eia608_cc_buffer (struct eia608_screen *data) +{ + int i; + + for (i=0;i<15;i++) + { + memset(data->characters[i],' ',CC608_SCREEN_WIDTH); + data->characters[i][CC608_SCREEN_WIDTH]=0; + memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1); + memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1); + data->row_used[i]=0; + } + data->empty=1; +} + +void init_eia608 (struct eia608 *data) +{ + data->cursor_column=0; + data->cursor_row=0; + clear_eia608_cc_buffer (&data->buffer1); + clear_eia608_cc_buffer (&data->buffer2); + data->visible_buffer=1; + data->last_c1=0; + data->last_c2=0; + data->mode=MODE_POPUP; + // data->current_visible_start_cc=0; + data->current_visible_start_ms=0; + data->srt_counter=0; + data->screenfuls_counter=0; + data->channel=1; + data->color=default_color; + data->font=FONT_REGULAR; + data->rollup_base_row=14; +} + +struct eia608_screen *get_writing_buffer (struct s_write *wb) +{ + struct eia608_screen *use_buffer=NULL; + switch (wb->data608->mode) + { + case MODE_POPUP: // Write on the non-visible buffer + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer2; + else + use_buffer = &wb->data608->buffer1; + break; + case MODE_ROLLUP_2: // Write directly to screen + case MODE_ROLLUP_3: + case MODE_ROLLUP_4: + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer1; + else + use_buffer = &wb->data608->buffer2; + break; + default: + fatal (EXIT_BUG_BUG, "Caption mode has an illegal value at get_writing_buffer(), this is a bug.\n"); + } + return use_buffer; +} + +void write_char (const unsigned char c, struct s_write *wb) +{ + if (wb->data608->mode!=MODE_TEXT) + { + struct eia608_screen * use_buffer=get_writing_buffer(wb); + /* hb_log ("\rWriting char [%c] at %s:%d:%d\n",c, + use_buffer == &wb->data608->buffer1?"B1":"B2", + wb->data608->cursor_row,wb->data608->cursor_column); */ + use_buffer->characters[wb->data608->cursor_row][wb->data608->cursor_column]=c; + use_buffer->colors[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->color; + use_buffer->fonts[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->font; + use_buffer->row_used[wb->data608->cursor_row]=1; + use_buffer->empty=0; + if (wb->data608->cursor_column<31) + wb->data608->cursor_column++; + } + +} + +/* Handle MID-ROW CODES. */ +void handle_text_attr (const unsigned char c1, const unsigned char c2, struct s_write *wb) +{ + // Handle channel change + wb->data608->channel=wb->new_channel; + if (wb->data608->channel!=cc_channel) + return; + if (debug_608) + hb_log ("\r608: text_attr: %02X %02X",c1,c2); + if ( ((c1!=0x11 && c1!=0x19) || + (c2<0x20 || c2>0x2f)) && debug_608) + { + hb_log ("\rThis is not a text attribute!\n"); + } + else + { + int i = c2-0x20; + wb->data608->color=pac2_attribs[i][0]; + wb->data608->font=pac2_attribs[i][1]; + if (debug_608) + hb_log (" -- Color: %s, font: %s\n", + color_text[wb->data608->color][0], + font_text[wb->data608->font]); + if (wb->data608->cursor_column<31) + wb->data608->cursor_column++; + } +} + +void mstotime (LLONG milli, unsigned *hours, unsigned *minutes, + unsigned *seconds, unsigned *ms) +{ + // LLONG milli = (LLONG) ((ccblock*1000)/29.97); + *ms=(unsigned) (milli%1000); // milliseconds + milli=(milli-*ms)/1000; // Remainder, in seconds + *seconds = (int) (milli%60); + milli=(milli-*seconds)/60; // Remainder, in minutes + *minutes = (int) (milli%60); + milli=(milli-*minutes)/60; // Remainder, in hours + *hours=(int) milli; +} + +void write_subtitle_file_footer (struct s_write *wb) +{ + switch (write_format) + { + case OF_SAMI: + sprintf ((char *) str,"</BODY></SAMI>\n"); + if (debug_608 && encoding!=ENC_UNICODE) + { + hb_log ("\r%s\n", str); + } + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str); + //fwrite (enc_buffer,enc_buffer_used,1,wb->fh); + XMLRPC_APPEND(enc_buffer,enc_buffer_used); + break; + default: // Nothing to do. Only SAMI has a footer + break; + } +} + +void fhb_log_encoded (FILE *fh, const char *string) +{ + GUARANTEE(strlen (string)*3); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) string); + fwrite (enc_buffer,enc_buffer_used,1,fh); +} + +void write_subtitle_file_header (struct s_write *wb) +{ + switch (write_format) + { + case OF_SRT: // Subrip subtitles have no header + break; + case OF_SAMI: // This header brought to you by McPoodle's CCASDI + //fhb_log_encoded (wb->fh, sami_header); + GUARANTEE(strlen (sami_header)*3); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) sami_header); + //fwrite (enc_buffer,enc_buffer_used,1,wb->fh); + XMLRPC_APPEND(enc_buffer,enc_buffer_used); + break; + case OF_RCWT: // Write header + //fwrite (rcwt_header, sizeof(rcwt_header),1,wb->fh); + break; + case OF_TRANSCRIPT: // No header. Fall thru + default: + break; + } +} + +void write_cc_line_as_transcript (struct eia608_screen *data, struct s_write *wb, int line_number) +{ + hb_buffer_t *buffer; + + if (sentence_cap) + { + capitalize(line_number,data, &wb->new_sentence); + correct_case(line_number,data); + } + int length = get_decoder_line_basic (wb->subline, line_number, data); + if (debug_608 && encoding!=ENC_UNICODE) + { + hb_log ("\r"); + hb_log ("%s\n",wb->subline); + } + if (length>0) + { + //fwrite (wb->subline, 1, length, wb->fh); + /* + * Put this subtitle in a hb_buffer_t and shove it into the subtitle fifo + */ + buffer = hb_buffer_init( strlen( wb->subline ) + 1 ); + buffer->start = wb->last_pts; + buffer->stop = wb->last_pts+1; + strcpy( buffer->data, wb->subline ); + //hb_log("CC %lld: %s", buffer->stop, wb->subline); + + hb_fifo_push( wb->subtitle->fifo_raw, buffer ); + + XMLRPC_APPEND(wb->subline,length); + //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh); + XMLRPC_APPEND(encoded_crlf,encoded_crlf_length); + } + // fhb_log (wb->fh,encoded_crlf); +} + +int write_cc_buffer_as_transcript (struct eia608_screen *data, struct s_write *wb) +{ + int i; + + int wrote_something = 0; + if (debug_608) + { + hb_log ("\n- - - TRANSCRIPT caption - - -\n"); + } + for (i=0;i<15;i++) + { + if (data->row_used[i]) + { + write_cc_line_as_transcript (data,wb, i); + } + wrote_something=1; + } + if (debug_608) + { + hb_log ("- - - - - - - - - - - -\r\n"); + } + return wrote_something; +} + +void write_cc_buffer_to_gui (struct eia608_screen *data, struct s_write *wb) +{ + unsigned h1,m1,s1,ms1; + unsigned h2,m2,s2,ms2; + int i; + + LLONG ms_start= wb->data608->current_visible_start_ms; + + ms_start+=subs_delay; + if (ms_start<0) // Drop screens that because of subs_delay start too early + return; + int time_reported=0; + for (i=0;i<15;i++) + { + if (data->row_used[i]) + { + hb_log ("###SUBTITLE#"); + if (!time_reported) + { + LLONG ms_end = get_fts()+subs_delay; + mstotime (ms_start,&h1,&m1,&s1,&ms1); + mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line. + // Note, only MM:SS here as we need to save space in the preview window + hb_log ("%02u:%02u#%02u:%02u#", + h1*60+m1,s1, h2*60+m2,s2); + time_reported=1; + } + else + hb_log ("##"); + + // We don't capitalize here because whatever function that was used + // before to write to file already took care of it. + int length = get_decoder_line_encoded_for_gui (wb->subline, i, data); + fwrite (wb->subline, 1, length, stderr); + fwrite ("\n",1,1,stderr); + } + } + fflush (stderr); +} + +int write_cc_buffer_as_srt (struct eia608_screen *data, struct s_write *wb) +{ + unsigned h1,m1,s1,ms1; + unsigned h2,m2,s2,ms2; + int wrote_something = 0; + LLONG ms_start= wb->data608->current_visible_start_ms; + int i; + + ms_start+=subs_delay; + if (ms_start<0) // Drop screens that because of subs_delay start too early + return 0; + + LLONG ms_end = get_fts()+subs_delay; + mstotime (ms_start,&h1,&m1,&s1,&ms1); + mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line. + char timeline[128]; + wb->data608->srt_counter++; + sprintf (timeline,"%u\r\n",wb->data608->srt_counter); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline); + fwrite (enc_buffer,enc_buffer_used,1,wb->fh); + XMLRPC_APPEND(enc_buffer,enc_buffer_used); + sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n", + h1,m1,s1,ms1, h2,m2,s2,ms2); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline); + if (debug_608) + { + hb_log ("\n- - - SRT caption - - -\n"); + hb_log (timeline); + } + fwrite (enc_buffer,enc_buffer_used,1,wb->fh); + XMLRPC_APPEND(enc_buffer,enc_buffer_used); + for (i=0;i<15;i++) + { + if (data->row_used[i]) + { + if (sentence_cap) + { + capitalize(i,data, &wb->new_sentence); + correct_case(i,data); + } + int length = get_decoder_line_encoded (wb->subline, i, data); + if (debug_608 && encoding!=ENC_UNICODE) + { + hb_log ("\r"); + hb_log ("%s\n",wb->subline); + } + fwrite (wb->subline, 1, length, wb->fh); + XMLRPC_APPEND(wb->subline,length); + fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh); + XMLRPC_APPEND(encoded_crlf,encoded_crlf_length); + wrote_something=1; + // fhb_log (wb->fh,encoded_crlf); + } + } + if (debug_608) + { + hb_log ("- - - - - - - - - - - -\r\n"); + } + // fhb_log (wb->fh, encoded_crlf); + fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh); + XMLRPC_APPEND(encoded_crlf,encoded_crlf_length); + return wrote_something; +} + +int write_cc_buffer_as_sami (struct eia608_screen *data, struct s_write *wb) +{ + int wrote_something=0; + LLONG startms = wb->data608->current_visible_start_ms; + int i; + + startms+=subs_delay; + if (startms<0) // Drop screens that because of subs_delay start too early + return 0; + + LLONG endms = get_fts()+subs_delay; + endms--; // To prevent overlapping with next line. + sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\">\r\n",startms); + if (debug_608 && encoding!=ENC_UNICODE) + { + hb_log ("\r%s\n", str); + } + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str); + fwrite (enc_buffer,enc_buffer_used,1,wb->fh); + XMLRPC_APPEND(enc_buffer,enc_buffer_used); + for (i=0;i<15;i++) + { + if (data->row_used[i]) + { + int length = get_decoder_line_encoded (wb->subline, i, data); + if (debug_608 && encoding!=ENC_UNICODE) + { + hb_log ("\r"); + hb_log ("%s\n",wb->subline); + } + fwrite (wb->subline, 1, length, wb->fh); + XMLRPC_APPEND(wb->subline,length); + wrote_something=1; + if (i!=14) + { + fwrite (encoded_br, 1, encoded_br_length,wb->fh); + XMLRPC_APPEND(encoded_br, encoded_br_length); + } + fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh); + XMLRPC_APPEND(encoded_crlf, encoded_crlf_length); + } + } + sprintf ((char *) str,"</P></SYNC>\r\n"); + if (debug_608 && encoding!=ENC_UNICODE) + { + hb_log ("\r%s\n", str); + } + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str); + fwrite (enc_buffer,enc_buffer_used,1,wb->fh); + XMLRPC_APPEND(enc_buffer,enc_buffer_used); + sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",endms); + if (debug_608 && encoding!=ENC_UNICODE) + { + hb_log ("\r%s\n", str); + } + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str); + fwrite (enc_buffer,enc_buffer_used,1,wb->fh); + XMLRPC_APPEND(enc_buffer,enc_buffer_used); + return wrote_something; +} + +struct eia608_screen *get_current_visible_buffer (struct s_write *wb) +{ + struct eia608_screen *data; + if (wb->data608->visible_buffer==1) + data = &wb->data608->buffer1; + else + data = &wb->data608->buffer2; + return data; +} + + +int write_cc_buffer (struct s_write *wb) +{ + struct eia608_screen *data; + int wrote_something=0; + if (screens_to_process!=-1 && wb->data608->screenfuls_counter>=screens_to_process) + { + // We are done. + processed_enough=1; + return 0; + } + if (wb->data608->visible_buffer==1) + data = &wb->data608->buffer1; + else + data = &wb->data608->buffer2; + + if (!data->empty) + { + wb->new_sentence=1; + switch (write_format) + { + case OF_SRT: + wrote_something = write_cc_buffer_as_srt (data, wb); + break; + case OF_SAMI: + wrote_something = write_cc_buffer_as_sami (data,wb); + break; + case OF_TRANSCRIPT: + wrote_something = write_cc_buffer_as_transcript (data,wb); + break; + default: + break; + } + if (wrote_something && gui_mode_reports) + write_cc_buffer_to_gui (data,wb); + } + return wrote_something; +} + +void roll_up(struct s_write *wb) +{ + struct eia608_screen *use_buffer; + int i, j; + + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer1; + else + use_buffer = &wb->data608->buffer2; + int keep_lines; + switch (wb->data608->mode) + { + case MODE_ROLLUP_2: + keep_lines=2; + break; + case MODE_ROLLUP_3: + keep_lines=3; + break; + case MODE_ROLLUP_4: + keep_lines=4; + break; + default: // Shouldn't happen + keep_lines=0; + break; + } + int firstrow=-1, lastrow=-1; + // Look for the last line used + int rows_now=0; // Number of rows in use right now + for (i=0;i<15;i++) + { + if (use_buffer->row_used[i]) + { + rows_now++; + if (firstrow==-1) + firstrow=i; + lastrow=i; + } + } + + if (debug_608) + hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow); + + if (lastrow==-1) // Empty screen, nothing to rollup + return; + + for (j=lastrow-keep_lines+1;j<lastrow; j++) + { + if (j>=0) + { + memcpy (use_buffer->characters[j],use_buffer->characters[j+1],CC608_SCREEN_WIDTH+1); + memcpy (use_buffer->colors[j],use_buffer->colors[j+1],CC608_SCREEN_WIDTH+1); + memcpy (use_buffer->fonts[j],use_buffer->fonts[j+1],CC608_SCREEN_WIDTH+1); + use_buffer->row_used[j]=use_buffer->row_used[j+1]; + } + } + for (j=0;j<(1+wb->data608->cursor_row-keep_lines);j++) + { + memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH); + memset(use_buffer->colors[j],COL_WHITE,CC608_SCREEN_WIDTH); + memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH); + use_buffer->characters[j][CC608_SCREEN_WIDTH]=0; + use_buffer->row_used[j]=0; + } + memset(use_buffer->characters[lastrow],' ',CC608_SCREEN_WIDTH); + memset(use_buffer->colors[lastrow],COL_WHITE,CC608_SCREEN_WIDTH); + memset(use_buffer->fonts[lastrow],FONT_REGULAR,CC608_SCREEN_WIDTH); + + use_buffer->characters[lastrow][CC608_SCREEN_WIDTH]=0; + use_buffer->row_used[lastrow]=0; + + // Sanity check + rows_now=0; + for (i=0;i<15;i++) + if (use_buffer->row_used[i]) + rows_now++; + if (rows_now>keep_lines) + hb_log ("Bug in roll_up, should have %d lines but I have %d.\n", + keep_lines, rows_now); +} + +void erase_memory (struct s_write *wb, int displayed) +{ + struct eia608_screen *buf; + if (displayed) + { + if (wb->data608->visible_buffer==1) + buf=&wb->data608->buffer1; + else + buf=&wb->data608->buffer2; + } + else + { + if (wb->data608->visible_buffer==1) + buf=&wb->data608->buffer2; + else + buf=&wb->data608->buffer1; + } + clear_eia608_cc_buffer (buf); +} + +int is_current_row_empty (struct s_write *wb) +{ + struct eia608_screen *use_buffer; + int i; + + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer1; + else + use_buffer = &wb->data608->buffer2; + for (i=0;i<CC608_SCREEN_WIDTH;i++) + { + if (use_buffer->characters[wb->data608->rollup_base_row][i]!=' ') + return 0; + } + return 1; +} + +/* Process GLOBAL CODES */ +void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct s_write *wb) +{ + // Handle channel change + wb->data608->channel=wb->new_channel; + if (wb->data608->channel!=cc_channel) + return; + + enum command_code command = COM_UNKNOWN; + if (c1==0x15) + c1=0x14; + if ((c1==0x14 || c1==0x1C) && c2==0x2C) + command = COM_ERASEDISPLAYEDMEMORY; + if ((c1==0x14 || c1==0x1C) && c2==0x20) + command = COM_RESUMECAPTIONLOADING; + if ((c1==0x14 || c1==0x1C) && c2==0x2F) + command = COM_ENDOFCAPTION; + if ((c1==0x17 || c1==0x1F) && c2==0x21) + command = COM_TABOFFSET1; + if ((c1==0x17 || c1==0x1F) && c2==0x22) + command = COM_TABOFFSET2; + if ((c1==0x17 || c1==0x1F) && c2==0x23) + command = COM_TABOFFSET3; + if ((c1==0x14 || c1==0x1C) && c2==0x25) + command = COM_ROLLUP2; + if ((c1==0x14 || c1==0x1C) && c2==0x26) + command = COM_ROLLUP3; + if ((c1==0x14 || c1==0x1C) && c2==0x27) + command = COM_ROLLUP4; + if ((c1==0x14 || c1==0x1C) && c2==0x2D) + command = COM_CARRIAGERETURN; + if ((c1==0x14 || c1==0x1C) && c2==0x2E) + command = COM_ERASENONDISPLAYEDMEMORY; + if ((c1==0x14 || c1==0x1C) && c2==0x21) + command = COM_BACKSPACE; + if ((c1==0x14 || c1==0x1C) && c2==0x2b) + command = COM_RESUMETEXTDISPLAY; + if (debug_608) + { + hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]); + } + switch (command) + { + case COM_BACKSPACE: + if (wb->data608->cursor_column>0) + { + wb->data608->cursor_column--; + get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' '; + } + break; + case COM_TABOFFSET1: + if (wb->data608->cursor_column<31) + wb->data608->cursor_column++; + break; + case COM_TABOFFSET2: + wb->data608->cursor_column+=2; + if (wb->data608->cursor_column>31) + wb->data608->cursor_column=31; + break; + case COM_TABOFFSET3: + wb->data608->cursor_column+=3; + if (wb->data608->cursor_column>31) + wb->data608->cursor_column=31; + break; + case COM_RESUMECAPTIONLOADING: + wb->data608->mode=MODE_POPUP; + break; + case COM_RESUMETEXTDISPLAY: + wb->data608->mode=MODE_TEXT; + break; + case COM_ROLLUP2: + if (wb->data608->mode==MODE_POPUP) + { + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + erase_memory (wb, 1); + } + if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb)) + { + if (debug_608) + hb_log ("Two RU2, current line not empty. Simulating a CR\n"); + handle_command(0x14, 0x2D, wb); + } + wb->data608->mode=MODE_ROLLUP_2; + erase_memory (wb, 0); + wb->data608->cursor_column=0; + wb->data608->cursor_row=wb->data608->rollup_base_row; + break; + case COM_ROLLUP3: + if (wb->data608->mode==MODE_POPUP) + { + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + erase_memory (wb, 1); + } + if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb)) + { + if (debug_608) + hb_log ("Two RU3, current line not empty. Simulating a CR\n"); + handle_command(0x14, 0x2D, wb); + } + wb->data608->mode=MODE_ROLLUP_3; + erase_memory (wb, 0); + wb->data608->cursor_column=0; + wb->data608->cursor_row=wb->data608->rollup_base_row; + break; + case COM_ROLLUP4: + if (wb->data608->mode==MODE_POPUP) + { + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + erase_memory (wb, 1); + } + if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb)) + { + if (debug_608) + hb_log ("Two RU4, current line not empty. Simulating a CR\n"); + handle_command(0x14, 0x2D, wb); + } + + wb->data608->mode=MODE_ROLLUP_4; + wb->data608->cursor_column=0; + wb->data608->cursor_row=wb->data608->rollup_base_row; + erase_memory (wb, 0); + break; + case COM_CARRIAGERETURN: + // In transcript mode, CR doesn't write the whole screen, to avoid + // repeated lines. + if (write_format==OF_TRANSCRIPT) + { + write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row); + } + else + { + if (norollup) + delete_all_lines_but_current (get_current_visible_buffer (wb), wb->data608->cursor_row); + if (write_cc_buffer(wb)) + wb->data608->screenfuls_counter++; + } + roll_up(wb); + wb->data608->current_visible_start_ms=get_fts(); + wb->data608->cursor_column=0; + break; + case COM_ERASENONDISPLAYEDMEMORY: + erase_memory (wb,0); + break; + case COM_ERASEDISPLAYEDMEMORY: + // Write it to disk before doing this, and make a note of the new + // time it became clear. + if (write_format==OF_TRANSCRIPT && + (wb->data608->mode==MODE_ROLLUP_2 || wb->data608->mode==MODE_ROLLUP_3 || + wb->data608->mode==MODE_ROLLUP_4)) + { + // In transcript mode we just write the cursor line. The previous lines + // should have been written already, so writing everything produces + // duplicate lines. + write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row); + } + else + { + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + } + erase_memory (wb,1); + wb->data608->current_visible_start_ms=get_fts(); + break; + case COM_ENDOFCAPTION: // Switch buffers + // The currently *visible* buffer is leaving, so now we know it's ending + // time. Time to actually write it to file. + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1; + wb->data608->current_visible_start_ms=get_fts(); + wb->data608->cursor_column=0; + wb->data608->cursor_row=0; + wb->data608->color=default_color; + wb->data608->font=FONT_REGULAR; + break; + default: + if (debug_608) + { + hb_log ("\rNot yet implemented.\n"); + } + break; + } +} + +void handle_end_of_data (struct s_write *wb) +{ + // We issue a EraseDisplayedMemory here so if there's any captions pending + // they get written to file. + handle_command (0x14, 0x2c, wb); // EDM +} + +void handle_double (const unsigned char c1, const unsigned char c2, struct s_write *wb) +{ + unsigned char c; + if (wb->data608->channel!=cc_channel) + return; + if (c2>=0x30 && c2<=0x3f) + { + c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here + if (debug_608) + hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c); + write_char(c,wb); + } +} + +/* Process EXTENDED CHARACTERS */ +unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_write *wb) +{ + // Handle channel change + if (wb->new_channel > 2) + { + wb->new_channel -= 2; + if (debug_608) + hb_log ("\nChannel correction, now %d\n", wb->new_channel); + } + wb->data608->channel=wb->new_channel; + if (wb->data608->channel!=cc_channel) + return 0; + + // For lo values between 0x20-0x3f + unsigned char c=0; + + if (debug_608) + hb_log ("\rExtended: %02X %02X\n",hi,lo); + if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13)) + { + switch (hi) + { + case 0x12: + c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here + break; + case 0x13: + c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here + break; + } + // This column change is because extended characters replace + // the previous character (which is sent for basic decoders + // to show something similar to the real char) + if (wb->data608->cursor_column>0) + wb->data608->cursor_column--; + + write_char (c,wb); + } + return 1; +} + +/* Process PREAMBLE ACCESS CODES (PAC) */ +void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb) +{ + // Handle channel change + if (wb->new_channel > 2) + { + wb->new_channel -= 2; + if (debug_608) + hb_log ("\nChannel correction, now %d\n", wb->new_channel); + } + wb->data608->channel=wb->new_channel; + if (wb->data608->channel!=cc_channel) + return; + + int row=rowdata[((c1<<1)&14)|((c2>>5)&1)]; + + if (debug_608) + hb_log ("\rPAC: %02X %02X",c1,c2); + + if (c2>=0x40 && c2<=0x5f) + { + c2=c2-0x40; + } + else + { + if (c2>=0x60 && c2<=0x7f) + { + c2=c2-0x60; + } + else + { + if (debug_608) + hb_log ("\rThis is not a PAC!!!!!\n"); + return; + } + } + int color=pac2_attribs[c2][0]; + int font=pac2_attribs[c2][1]; + int indent=pac2_attribs[c2][2]; + if (debug_608) + hb_log (" -- Position: %d:%d, color: %s, font: %s\n",row, + indent,color_text[color][0],font_text[font]); + if (wb->data608->mode!=MODE_TEXT) + { + // According to Robson, row info is discarded in text mode + // but column is accepted + wb->data608->cursor_row=row-1 ; // Since the array is 0 based + } + wb->data608->rollup_base_row=row-1; + wb->data608->cursor_column=indent; +} + + +void handle_single (const unsigned char c1, struct s_write *wb) +{ + if (c1<0x20 || wb->data608->channel!=cc_channel) + return; // We don't allow special stuff here + if (debug_608) + hb_log ("%c",c1); + + /*if (debug_608) + hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */ + write_char (c1,wb); +} + +int check_channel (unsigned char c1, struct s_write *wb) +{ + if (c1==0x14) + { + if (debug_608 && wb->data608->channel!=1) + hb_log ("\nChannel change, now 1\n"); + return 1; + } + if (c1==0x1c) + { + if (debug_608 && wb->data608->channel!=2) + hb_log ("\nChannel change, now 2\n"); + return 2; + } + if (c1==0x15) + { + if (debug_608 && wb->data608->channel!=3) + hb_log ("\nChannel change, now 3\n"); + return 3; + } + if (c1==0x1d) + { + if (debug_608 && wb->data608->channel!=4) + hb_log ("\nChannel change, now 4\n"); + return 4; + } + + // Otherwise keep the current channel + return wb->data608->channel; +} + +/* Handle Command, special char or attribute and also check for +* channel changes. +* Returns 1 if something was written to screen, 0 otherwise */ +int disCommand (unsigned char hi, unsigned char lo, struct s_write *wb) +{ + int wrote_to_screen=0; + + /* Full channel changes are only allowed for "GLOBAL CODES", + * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES", + * "MID-ROW CODES". + * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and + * SPECIAL/SPECIAL CHARACTERS allow only switching + * between 1&3 or 2&4. */ + wb->new_channel = check_channel (hi,wb); + //if (wb->data608->channel!=cc_channel) + // continue; + + if (hi>=0x18 && hi<=0x1f) + hi=hi-8; + + switch (hi) + { + case 0x10: + if (lo>=0x40 && lo<=0x5f) + handle_pac (hi,lo,wb); + break; + case 0x11: + if (lo>=0x20 && lo<=0x2f) + handle_text_attr (hi,lo,wb); + if (lo>=0x30 && lo<=0x3f) + { + wrote_to_screen=1; + handle_double (hi,lo,wb); + } + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + case 0x12: + case 0x13: + if (lo>=0x20 && lo<=0x3f) + { + wrote_to_screen=handle_extended (hi,lo,wb); + } + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + case 0x14: + case 0x15: + if (lo>=0x20 && lo<=0x2f) + handle_command (hi,lo,wb); + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + case 0x16: + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + case 0x17: + if (lo>=0x21 && lo<=0x22) + handle_command (hi,lo,wb); + if (lo>=0x2e && lo<=0x2f) + handle_text_attr (hi,lo,wb); + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + } + return wrote_to_screen; +} + +void process608 (const unsigned char *data, int length, struct s_write *wb) +{ + static int textprinted = 0; + int i; + + if (data!=NULL) + { + for (i=0;i<length;i=i+2) + { + unsigned char hi, lo; + int wrote_to_screen=0; + hi = data[i] & 0x7F; // Get rid of parity bit + lo = data[i+1] & 0x7F; // Get rid of parity bit + + if (hi==0 && lo==0) // Just padding + continue; + // hb_log ("\r[%02X:%02X]\n",hi,lo); + + if (hi>=0x01 && hi<=0x0E) + { + // XDS crap - mode. Would be nice to support it eventually + // wb->data608->last_c1=0; + // wb->data608->last_c2=0; + wb->data608->channel=3; // Always channel 3 + wb->in_xds_mode=1; + } + if (hi==0x0F) // End of XDS block + { + wb->in_xds_mode=0; + continue; + } + if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char + // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML + // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML + { + // We were writing characters before, start a new line for + // diagnostic output from disCommand() + if (debug_608 && textprinted == 1 ) + { + hb_log("\n"); + textprinted = 0; + } + + wb->in_xds_mode=0; // Back to normal + if (wb->data608->last_c1==hi && wb->data608->last_c2==lo) + { + // Duplicate dual code, discard + continue; + } + wb->data608->last_c1=hi; + wb->data608->last_c2=lo; + wrote_to_screen=disCommand (hi,lo,wb); + } + if (hi>=0x20) // Standard characters (always in pairs) + { + // Only print if the channel is active + if (wb->data608->channel!=cc_channel) + continue; + + if (debug_608) + { + if( textprinted == 0 ) + { + hb_log("\n"); + textprinted = 1; + } + } + + handle_single(hi,wb); + handle_single(lo,wb); + wrote_to_screen=1; + wb->data608->last_c1=0; + wb->data608->last_c2=0; + } + + if ( debug_608 && !textprinted && wb->data608->channel==cc_channel ) + { // Current FTS information after the characters are shown + //hb_log("Current FTS: %s\n", print_mstime(get_fts())); + } + + if (wrote_to_screen && direct_rollup && // If direct_rollup is enabled and + (wb->data608->mode==MODE_ROLLUP_2 || // we are in rollup mode, write now. + wb->data608->mode==MODE_ROLLUP_3 || + wb->data608->mode==MODE_ROLLUP_4)) + { + // We don't increase screenfuls_counter here. + write_cc_buffer (wb); + wb->data608->current_visible_start_ms=get_fts(); + } + } + } +} + + +/* Return a pointer to a string that holds the printable characters + * of the caption data block. FOR DEBUG PURPOSES ONLY! */ +unsigned char *debug_608toASC (unsigned char *cc_data, int channel) +{ + static unsigned char output[3]; + + unsigned char cc_valid = (cc_data[0] & 4) >>2; + unsigned char cc_type = cc_data[0] & 3; + unsigned char hi, lo; + + output[0]=' '; + output[1]=' '; + output[2]='\x00'; + + if (cc_valid && cc_type==channel) + { + hi = cc_data[1] & 0x7F; // Get rid of parity bit + lo = cc_data[2] & 0x7F; // Get rid of parity bit + if (hi>=0x20) + { + output[0]=hi; + output[1]=(lo>=20 ? lo : '.'); + output[2]='\x00'; + } + else + { + output[0]='<'; + output[1]='>'; + output[2]='\x00'; + } + } + return output; +} diff --git a/libhb/deccc608sub.h b/libhb/deccc608sub.h new file mode 100644 index 000000000..e6c1518a7 --- /dev/null +++ b/libhb/deccc608sub.h @@ -0,0 +1,131 @@ +/* + * From ccextractor, leave this file as intact and close to the original as possible so that + * it is easy to patch in fixes - even though this file contains code that we don't need. + * + * Note that the SRT sub generation from CC could be useful for mkv subs. + */ +#ifndef __deccc608sub_H__ +#define __deccc608sub_H__ + +#include "common.h" + +struct s_write; + +void handle_end_of_data (struct s_write *wb); +void process608 (const unsigned char *data, int length, struct s_write *wb); +void get_char_in_latin_1 (unsigned char *buffer, unsigned char c); +void get_char_in_unicode (unsigned char *buffer, unsigned char c); +int get_char_in_utf_8 (unsigned char *buffer, unsigned char c); +unsigned char cctolower (unsigned char c); +unsigned char cctoupper (unsigned char c); +int general_608_init (struct s_write *wb); +void general_608_close (struct s_write *wb); + +#define CC608_SCREEN_WIDTH 32 + +enum cc_modes +{ + MODE_POPUP = 0, + MODE_ROLLUP_2 = 1, + MODE_ROLLUP_3 = 2, + MODE_ROLLUP_4 = 3, + MODE_TEXT = 4 +}; + +enum color_code +{ + COL_WHITE = 0, + COL_GREEN = 1, + COL_BLUE = 2, + COL_CYAN = 3, + COL_RED = 4, + COL_YELLOW = 5, + COL_MAGENTA = 6, + COL_USERDEFINED = 7 +}; + + +enum font_bits +{ + FONT_REGULAR = 0, + FONT_ITALICS = 1, + FONT_UNDERLINED = 2, + FONT_UNDERLINED_ITALICS = 3 +}; + + +struct eia608_screen // A CC buffer +{ + unsigned char characters[15][33]; + unsigned char colors[15][33]; + unsigned char fonts[15][33]; // Extra char at the end for a 0 + int row_used[15]; // Any data in row? + int empty; // Buffer completely empty? +}; + +#define LLONG long long + +struct eia608 +{ + struct eia608_screen buffer1; + struct eia608_screen buffer2; + int cursor_row, cursor_column; + int visible_buffer; + int srt_counter; // Number of subs currently written + int screenfuls_counter; // Number of meaningful screenfuls written + LLONG current_visible_start_ms; // At what time did the current visible buffer became so? + // unsigned current_visible_start_cc; // At what time did the current visible buffer became so? + enum cc_modes mode; + unsigned char last_c1, last_c2; + int channel; // Currently selected channel + unsigned char color; // Color we are currently using to write + unsigned char font; // Font we are currently using to write + int rollup_base_row; +}; + +struct s_write { + struct eia608 *data608; + FILE *fh; + unsigned char *subline; + int new_sentence; + int new_channel; + int in_xds_mode; + hb_subtitle_t * subtitle; + uint64_t last_pts; +}; + +enum command_code +{ + COM_UNKNOWN = 0, + COM_ERASEDISPLAYEDMEMORY = 1, + COM_RESUMECAPTIONLOADING = 2, + COM_ENDOFCAPTION = 3, + COM_TABOFFSET1 = 4, + COM_TABOFFSET2 = 5, + COM_TABOFFSET3 = 6, + COM_ROLLUP2 = 7, + COM_ROLLUP3 = 8, + COM_ROLLUP4 = 9, + COM_CARRIAGERETURN = 10, + COM_ERASENONDISPLAYEDMEMORY = 11, + COM_BACKSPACE = 12, + COM_RESUMETEXTDISPLAY = 13 +}; + +enum encoding_type +{ + ENC_UNICODE = 0, + ENC_LATIN_1 = 1, + ENC_UTF_8 = 2 +}; + +enum output_format +{ + OF_RAW = 0, + OF_SRT = 1, + OF_SAMI = 2, + OF_TRANSCRIPT = 3, + OF_RCWT = 4 +}; + +#endif diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c index 6ad6127d6..9d28c9775 100644 --- a/libhb/decmpeg2.c +++ b/libhb/decmpeg2.c @@ -7,6 +7,7 @@ #include "hb.h" #include "hbffmpeg.h" #include "mpeg2dec/mpeg2.h" +#include "deccc608sub.h" /* Cadence tracking */ #ifndef PIC_FLAG_REPEAT_FIRST_FIELD @@ -34,6 +35,7 @@ typedef struct hb_libmpeg2_s mpeg2dec_t * libmpeg2; const mpeg2_info_t * info; hb_job_t * job; + hb_title_t * title; int width; int height; int rate; @@ -45,6 +47,8 @@ typedef struct hb_libmpeg2_s int64_t last_pts; int cadence[12]; int flag; + struct s_write cc608; /* Closed Captions */ + hb_subtitle_t * subtitle; } hb_libmpeg2_t; /********************************************************************** @@ -60,9 +64,60 @@ static hb_libmpeg2_t * hb_libmpeg2_init() m->info = mpeg2_info( m->libmpeg2 ); m->last_pts = -1; + /* Closed Captions init, whether needed or not */ + general_608_init( &m->cc608 ); + m->cc608.data608 = calloc(1, sizeof(struct eia608)); return m; } +static void hb_mpeg2_cc( hb_libmpeg2_t * m, uint8_t *cc_block ) +{ + uint8_t cc_valid = (*cc_block & 4) >>2; + uint8_t cc_type = *cc_block & 3; + + if( !m->job ) + { + /* + * Ignore CC decoding during scanning. + */ + return; + } + + if (cc_valid || cc_type==3) + { + switch (cc_type) + { + case 0: + // CC1 stream + process608( cc_block+1, 2, &m->cc608 ); + break; + case 1: + // CC2 stream + //process608( cc_block+1, 2, &m->cc608 ); + break; + case 2: //EIA-708 + // DTVCC packet data + // Fall through + case 3: //EIA-708 + { + uint8_t temp[4]; + temp[0]=cc_valid; + temp[1]=cc_type; + temp[2]=cc_block[1]; + temp[3]=cc_block[2]; + //do_708 ((const unsigned char *) temp, 4); + } + break; + default: + break; + } + } + else + { + hb_log("Ignoring invalid CC block"); + } +} + static hb_buffer_t *hb_copy_frame( hb_job_t *job, int width, int height, uint8_t* y, uint8_t *u, uint8_t *v ) { @@ -128,7 +183,7 @@ static hb_buffer_t *hb_copy_frame( hb_job_t *job, int width, int height, * *********************************************************************/ static int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, - hb_list_t * list_raw ) + hb_list_t * list_raw ) { mpeg2_state_t state; hb_buffer_t * buf; @@ -345,6 +400,116 @@ static int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, { mpeg2_reset( m->libmpeg2, 0 ); } + + /* + * Look for Closed Captions if scanning (!job) or if closed captions have been requested. + */ + if( ( !m->job || m->subtitle) && + ( m->info->user_data_len != 0 && + m->info->user_data[0] == 0x43 && + m->info->user_data[1] == 0x43 ) ) + { + int i, j; + const uint8_t *header = &m->info->user_data[4]; + uint8_t pattern=header[0] & 0x80; + int field1packet = 0; /* expect Field 1 first */ + if (pattern==0x00) + field1packet=1; /* expect Field 1 second */ + int capcount=(header[0] & 0x1e) / 2; + header++; + + m->cc608.last_pts = m->last_pts; + + /* + * Add closed captions to the title if we are scanning (no job). + * + * Just because we don't add this doesn't mean that there aren't any when + * we scan, just that noone said anything. So you should be able to add + * closed captions some other way (See decmpeg2Init() for alternative + * approach of assuming that there are always CC, which is probably + * safer - however I want to leave the autodetect in here for now to + * see how it goes). + */ + if( !m->job && m->title ) + { + hb_subtitle_t * subtitle; + int found = 0; + int i; + + for( i = 0; i < hb_list_count( m->title->list_subtitle ); i++ ) + { + subtitle = hb_list_item( m->title->list_subtitle, i); + if( subtitle && subtitle->source == CCSUB ) + { + found = 1; + break; + } + } + + if( !found ) + { + subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); + subtitle->track = 0; + subtitle->id = 0x0; + snprintf( subtitle->lang, sizeof( subtitle->lang ), "Closed Captions"); + snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "und"); + subtitle->format = TEXTSUB; + subtitle->source = CCSUB; + subtitle->dest = PASSTHRUSUB; + subtitle->type = 5; + + hb_list_add( m->title->list_subtitle, subtitle ); + } + } + + for( i=0; i<capcount; i++ ) + { + for( j=0; j<2; j++ ) + { + uint8_t data[3]; + data[0] = header[0]; + data[1] = header[1]; + data[2] = header[2]; + header += 3; + /* Field 1 and 2 data can be in either order, + with marker bytes of \xff and \xfe + Since markers can be repeated, use pattern as well */ + if( data[0] == 0xff && j == field1packet ) + { + data[0] = 0x04; // Field 1 + } + else + { + data[0] = 0x05; // Field 2 + } + hb_mpeg2_cc( m, data ); + } + } + // Deal with extra closed captions some DVD have. + while( header[0]==0xfe || header[0]==0xff ) + { + for( j=0; j<2; j++ ) + { + uint8_t data[3]; + data[0] = header[0]; + data[1] = header[1]; + data[2] = header[2]; + header += 3; + /* Field 1 and 2 data can be in either order, + with marker bytes of \xff and \xfe + Since markers can be repeated, use pattern as well */ + if( data[0] == 0xff && j == field1packet ) + { + data[0] = 0x04; // Field 1 + } + else + { + data[0] = 0x05; // Field 2 + } + hb_mpeg2_cc( m, data ); + } + } + } } return 1; } @@ -360,6 +525,9 @@ static void hb_libmpeg2_close( hb_libmpeg2_t ** _m ) mpeg2_close( m->libmpeg2 ); + free( m->cc608.data608 ); + general_608_close( &m->cc608 ); + free( m ); *_m = NULL; } @@ -392,6 +560,69 @@ static int decmpeg2Init( hb_work_object_t * w, hb_job_t * job ) pv->libmpeg2->job = job; + if( job && job->title ) { + pv->libmpeg2->title = job->title; + } + + /* + * If not scanning, then are we supposed to extract Closed Captions? + */ + if( job ) + { + hb_subtitle_t * subtitle; + int i; + + for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) + { + subtitle = hb_list_item( job->list_subtitle, i); + if( subtitle && subtitle->source == CCSUB ) + { + pv->libmpeg2->subtitle = subtitle; + pv->libmpeg2->cc608.subtitle = subtitle; + break; + } + } + + } + + /* + * During a scan add a Closed Caption subtitle track to the title, + * since we may have CC. Don't bother actually trying to detect CC + * since we'd have to go through too much of the source. + * + if( !job && w->title ) + { + hb_subtitle_t * subtitle; + int found = 0; + int i; + + for( i = 0; i < hb_list_count( w->title->list_subtitle ); i++ ) + { + subtitle = hb_list_item( w->title->list_subtitle, i); + if( subtitle && subtitle->source == CCSUB ) + { + found = 1; + break; + } + } + + if( !found ) + { + subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); + subtitle->track = 0; + subtitle->id = 0x0; + snprintf( subtitle->lang, sizeof( subtitle->lang ), "Closed Captions"); + snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "und"); + subtitle->format = TEXTSUB; + subtitle->source = CCSUB; + subtitle->dest = PASSTHRUSUB; + subtitle->type = 5; + + hb_list_add( w->title->list_subtitle, subtitle ); + } + } + */ + return 0; } @@ -401,12 +632,16 @@ static int decmpeg2Init( hb_work_object_t * w, hb_job_t * job ) * *********************************************************************/ static int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in, - hb_buffer_t ** buf_out ) + hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * buf, * last = NULL; int status = HB_WORK_OK; + if( w->title && pv && pv->libmpeg2 && !pv->libmpeg2->title ) { + pv->libmpeg2->title = w->title; + } + // The reader found a chapter break, consume it completely, and remove it from the // stream. We need to shift it. if( (*buf_in)->new_chap ) diff --git a/libhb/decsub.c b/libhb/decvobsub.c index 98ffa1a0d..98ffa1a0d 100644 --- a/libhb/decsub.c +++ b/libhb/decvobsub.c diff --git a/libhb/muxcommon.c b/libhb/muxcommon.c index b6008dc3c..30536dd00 100644 --- a/libhb/muxcommon.c +++ b/libhb/muxcommon.c @@ -223,22 +223,28 @@ static void MuxerFunc( void * _mux ) { switch( job->mux ) { - case HB_MUX_MP4: - case HB_MUX_PSP: - case HB_MUX_IPOD: - m = hb_mux_mp4_init( job ); - break; - case HB_MUX_AVI: - m = hb_mux_avi_init( job ); - break; - case HB_MUX_OGM: - m = hb_mux_ogm_init( job ); - break; - case HB_MUX_MKV: - m = hb_mux_mkv_init( job ); + case HB_MUX_MP4: + case HB_MUX_PSP: + case HB_MUX_IPOD: + m = hb_mux_mp4_init( job ); + break; + case HB_MUX_AVI: + m = hb_mux_avi_init( job ); + break; + case HB_MUX_OGM: + m = hb_mux_ogm_init( job ); + break; + case HB_MUX_MKV: + m = hb_mux_mkv_init( job ); + default: + hb_error( "No muxer selected, exiting" ); + *job->die = 1; } /* Create file, write headers */ - m->init( m ); + if( m ) + { + m->init( m ); + } } /* Build list of fifos we're interested in */ @@ -254,8 +260,8 @@ static void MuxerFunc( void * _mux ) // The following 'while' is the main muxing loop. - int thread_sleep_interval = 50; - while( !*job->die ) + int thread_sleep_interval = 50; + while( !*job->die ) { MoveToInternalFifos( mux ); if ( mux->rdy != mux->allRdy ) @@ -316,10 +322,13 @@ finished: /* Update the UI */ hb_state_t state; state.state = HB_STATE_MUXING; - p.progress = 0; + p.progress = 0; hb_set_state( job->h, &state ); #undef p - m->end( m ); + if( m ) + { + m->end( m ); + } if( !stat( job->file, &sb ) ) { @@ -353,8 +362,11 @@ finished: } } } - - free( m ); + + if( m ) + { + free( m ); + } for( i = 0; i < mux->ntracks; ++i ) { diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c index 29d08ca88..81ee2f8ad 100644 --- a/libhb/muxmp4.c +++ b/libhb/muxmp4.c @@ -590,6 +590,7 @@ static int MP4End( hb_mux_object_t * m ) { hb_job_t * job = m->job; hb_title_t * title = job->title; + int i; /* Write our final chapter marker */ if( m->job->chapter_markers ) @@ -666,6 +667,37 @@ static int MP4End( hb_mux_object_t * m ) MP4TagsFree( tags ); } + /* + * Display any text subs. + * + * This is placeholder code, what needs to happen is that we need to + * convert these PTS (which are pre-sync ones) into HB timestamps, + * I guess sync should do that? + * + * And then this needs to move into the Mux code above and insert + * subtitle samples into the MP4 at the correct times. + */ + for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) + { + hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i ); + + if( subtitle && subtitle->format == TEXTSUB && + subtitle->dest == PASSTHRUSUB ) + { + /* + * Should be adding this one if the timestamp is right. + */ + hb_buffer_t *buffer; + + while( (buffer = hb_fifo_get( subtitle->fifo_raw )) != NULL ) + { + hb_log("MuxMP4: Text Sub: %s", buffer->data); + + hb_buffer_close( &buffer ); + } + } + } + MP4Close( m->file ); if ( job->mp4_optimize ) diff --git a/libhb/scan.c b/libhb/scan.c index 420904b61..6ff83a872 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -440,6 +440,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2; hb_work_object_t *vid_decoder = hb_get_work( vcodec ); vid_decoder->codec_param = title->video_codec_param; + vid_decoder->title = title; vid_decoder->init( vid_decoder, NULL ); hb_buffer_t * vid_buf = NULL; int vidskip = 0; diff --git a/libhb/work.c b/libhb/work.c index 8c6db7d54..b413c78fe 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -272,13 +272,16 @@ void hb_display_job_info( hb_job_t * job ) } } - for( i=0; i < hb_list_count(title->list_subtitle); i++ ) + for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) { subtitle = hb_list_item( title->list_subtitle, i ); if( subtitle ) { - hb_log( " * subtitle track %i, %s (id %x)", subtitle->track, subtitle->lang, subtitle->id); + hb_log( " * subtitle track %i, %s (id %x) %s [%s] -> %s ", subtitle->track, subtitle->lang, subtitle->id, + subtitle->format == PICTURESUB ? "Picture" : "Text", + subtitle->source == VOBSUB ? "VOBSUB" : (subtitle->source == CCSUB ? "CC" : "SRT"), + subtitle->dest == RENDERSUB ? "Render/Burn in" : "Pass-Through"); } } |