OSDN Git Service

Soft Subs Part 2: Auto-detect CC during scan, add CC to subtitle list in title, if...
authoreddyg <eddyg@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Mon, 4 May 2009 04:56:19 +0000 (04:56 +0000)
committereddyg <eddyg@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Mon, 4 May 2009 04:56:19 +0000 (04:56 +0000)
git-svn-id: svn://localhost/HandBrake/trunk@2375 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.h
libhb/deccc608sub.c [new file with mode: 0644]
libhb/deccc608sub.h [new file with mode: 0644]
libhb/decmpeg2.c
libhb/decvobsub.c [moved from libhb/decsub.c with 100% similarity]
libhb/muxcommon.c
libhb/muxmp4.c
libhb/scan.c
libhb/work.c
test/test.c

index f195110..6a2c8d5 100644 (file)
@@ -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 (file)
index 0000000..2b0b199
--- /dev/null
@@ -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\">&nbsp;</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 (file)
index 0000000..e6c1518
--- /dev/null
@@ -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
index 6ad6127..9d28c97 100644 (file)
@@ -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 )
similarity index 100%
rename from libhb/decsub.c
rename to libhb/decvobsub.c
index b6008dc..30536dd 100644 (file)
@@ -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 )
     {
index 29d08ca..81ee2f8 100644 (file)
@@ -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 )
index 420904b..6ff83a8 100644 (file)
@@ -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;
index 8c6db7d..b413c78 100644 (file)
@@ -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");
         }
     }
 
index 411c20c..a1d9262 100644 (file)
@@ -1645,9 +1645,11 @@ static int HandleEvents( hb_handle_t * h )
                  * Find the subtitle with the same track as "sub" and
                  * add that to the job subtitle list
                  */
-                subtitle = hb_list_item( title->list_subtitle, sub );
+                subtitle = hb_list_item( title->list_subtitle, sub-1 );
                 if( subtitle ) {
                     hb_list_add( job->list_subtitle, subtitle );
+                } else {
+                    fprintf( stderr, "Could not find subtitle track %d, skipped\n", sub );
                 }
             }