/*
* 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_SRT;
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(struct s_write *wb)
{
return wb->last_pts;
}
#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 \n\
\n\
\n\n\
\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",""},
{"blue",""},
{"cyan",""},
{"red",""},
{"yellow",""},
{"magenta",""},
{"userdefined","subline) {
wb->subline = malloc(2048);
if (!wb->subline)
{
return -1;
}
}
wb->new_sentence = 1;
wb->new_channel = 1;
wb->in_xds_mode = 0;
wb->hb_buffer = NULL;
wb->hb_last_buffer = NULL;
wb->last_pts = 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);
}
if( wb->hb_buffer ) {
hb_buffer_close( &wb->hb_buffer );
}
}
#include
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 (icharacters[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;icharacters[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;icharacters[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 *) "");
}
// 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
// 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 *) "");
}
if (is_underlined==0 && underlined) // Close underline
{
buffer+=encode_line (buffer, (unsigned char *) "");
}
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 *) "");
}
if (has_ita==0 && italics) // Close italics
{
buffer+=encode_line (buffer, (unsigned char *) "");
}
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 *) "");
}
if (underlined)
{
buffer+=encode_line (buffer, (unsigned char *) "");
}
if (col != COL_WHITE && !nofontcolor)
{
buffer+=encode_line (buffer, (unsigned char *) "");
}
*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,"\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( length + 1 );
buffer->start = wb->data608->current_visible_start_ms;
buffer->stop = get_fts(wb);
memcpy( buffer->data, wb->subline, length + 1 );
//hb_log("CC %lld: %s", buffer->stop, wb->subline);
if (wb->hb_last_buffer) {
wb->hb_last_buffer->next = buffer;
} else {
wb->hb_buffer = buffer;
}
wb->hb_last_buffer = 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(wb)+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(wb)+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);
}
if (length > 0)
{
hb_buffer_t *buffer = hb_buffer_init( length + 1 );
buffer->start = ms_start;
buffer->stop = ms_end;
memcpy( buffer->data, wb->subline, length + 1 );
if (wb->hb_last_buffer) {
wb->hb_last_buffer->next = buffer;
} else {
wb->hb_buffer = buffer;
}
wb->hb_last_buffer = buffer;
//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(wb)+subs_delay;
endms--; // To prevent overlapping with next line.
sprintf ((char *) str,"\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,"
\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,"
\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=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;icharacters[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);
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(wb);
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);
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=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(wb);
}
}
}
}
/* 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;
}
struct hb_work_private_s
{
hb_job_t * job;
struct s_write * cc608;
};
int decccInit( hb_work_object_t * w, hb_job_t * job )
{
int retval = 1;
hb_work_private_t * pv;
pv = calloc( 1, sizeof( hb_work_private_t ) );
if( pv )
{
w->private_data = pv;
pv->job = job;
pv->cc608 = calloc(1, sizeof(struct s_write));
if( pv->cc608 )
{
retval = general_608_init(pv->cc608);
if( !retval )
{
pv->cc608->data608 = calloc(1, sizeof(struct eia608));
if( !pv->cc608->data608 )
{
retval = 1;
}
}
}
}
return retval;
}
int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
hb_buffer_t * in = *buf_in;
if ( in->size <= 0 )
{
/* EOF on input stream - send it downstream & say that we're done */
handle_end_of_data(pv->cc608);
/*
* Grab any pending buffer and output them with the EOF on the end
*/
if (pv->cc608->hb_last_buffer) {
pv->cc608->hb_last_buffer->next = in;
*buf_out = pv->cc608->hb_buffer;
*buf_in = NULL;
pv->cc608->hb_buffer = NULL;
pv->cc608->hb_last_buffer = NULL;
} else {
*buf_out = in;
*buf_in = NULL;
}
return HB_WORK_DONE;
}
pv->cc608->last_pts = in->start;
process608(in->data, in->size, pv->cc608);
/*
* If there is one waiting then pass it on
*/
*buf_out = pv->cc608->hb_buffer;
pv->cc608->hb_buffer = NULL;
pv->cc608->hb_last_buffer = NULL;
return HB_WORK_OK;
}
void decccClose( hb_work_object_t * w )
{
hb_work_private_t * pv = w->private_data;
general_608_close( pv->cc608 );
free( pv->cc608->data608 );
free( pv->cc608 );
free( w->private_data );
}
hb_work_object_t hb_deccc608 =
{
WORK_DECCC608,
"Closed Caption (608) decoder",
decccInit,
decccWork,
decccClose
};