summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libhb/common.c113
-rw-r--r--libhb/common.h1
-rw-r--r--libhb/deccc608sub.c1562
-rw-r--r--libhb/deccc608sub.h48
-rw-r--r--libhb/decsrtsub.c182
-rw-r--r--libhb/decsrtsub.h16
-rw-r--r--libhb/decssasub.c462
-rw-r--r--libhb/decssasub.h35
-rw-r--r--libhb/dectx3gsub.c233
-rw-r--r--libhb/decutf8sub.c31
-rw-r--r--libhb/muxavformat.c66
-rw-r--r--libhb/muxcommon.c346
-rw-r--r--libhb/muxmkv.c14
-rw-r--r--libhb/rendersub.c203
14 files changed, 1377 insertions, 1935 deletions
diff --git a/libhb/common.c b/libhb/common.c
index b689facd4..8e9c02f22 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -824,7 +824,7 @@ int hb_audio_bitrate_get_default(uint32_t codec, int samplerate, int mixdown)
case HB_ACODEC_FFFLAC:
case HB_ACODEC_FFFLAC24:
goto fail;
-
+
// 96, 224, 640 Kbps
case HB_ACODEC_AC3:
bitrate = (nchannels * 128) - (32 * (nchannels < 5));
@@ -1323,14 +1323,14 @@ void hb_audio_compression_get_limits(uint32_t codec, float *low, float *high,
*high = 12.;
*low = 0.;
break;
-
+
case HB_ACODEC_LAME:
*direction = 1;
*granularity = 1.;
*high = 9.;
*low = 0.;
break;
-
+
default:
*direction = 0;
*granularity = 1.;
@@ -2798,9 +2798,9 @@ void hb_error( char * log, ... )
{
hb_unlock( mutex );
return;
- }
+ }
}
-
+
/*
* A new error, or the same one more than 10sec since the last one
* did we have any of the same counted up?
@@ -2808,7 +2808,7 @@ void hb_error( char * log, ... )
if( last_error_count > 0 )
{
/*
- * Print out the last error to ensure context for the last
+ * Print out the last error to ensure context for the last
* repeated message.
*/
if( error_handler )
@@ -2817,16 +2817,16 @@ void hb_error( char * log, ... )
} else {
hb_log( "%s", last_string );
}
-
+
if( last_error_count > 1 )
{
/*
* Only print out the repeat message for more than 2 of the
* same, since we just printed out two of them already.
*/
- snprintf( rep_string, 180, "Last error repeated %d times",
+ snprintf( rep_string, 180, "Last error repeated %d times",
last_error_count - 1 );
-
+
if( error_handler )
{
error_handler( rep_string );
@@ -2834,7 +2834,7 @@ void hb_error( char * log, ... )
hb_log( "%s", rep_string );
}
}
-
+
last_error_count = 0;
}
@@ -2936,7 +2936,7 @@ void hb_title_close( hb_title_t ** _t )
hb_subtitle_close( &subtitle );
}
hb_list_close( &t->list_subtitle );
-
+
while( ( attachment = hb_list_item( t->list_attachment, 0 ) ) )
{
hb_list_rem( t->list_attachment, attachment );
@@ -3565,7 +3565,7 @@ int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg)
/* Set the job's "in track" to the value passed in audiocfg.
* HandBrakeCLI assumes this value is preserved in the jobs
- * audio list, but in.track in the title's audio list is not
+ * audio list, but in.track in the title's audio list is not
* required to be the same. */
audio->config.in.track = audiocfg->in.track;
@@ -3687,6 +3687,39 @@ void hb_subtitle_close( hb_subtitle_t **sub )
**********************************************************************
*
*********************************************************************/
+int hb_subtitle_add_ssa_header(hb_subtitle_t *subtitle, int w, int h)
+{
+ // Free any pre-existing extradata
+ free(subtitle->extradata);
+
+ int fs = h * .066;
+
+ // SRT subtitles are represented internally as SSA
+ // Create an SSA header
+ const char * ssa_header =
+ "[Script Info]\r\n"
+ "ScriptType: v4.00+\r\n"
+ "Collisions: Normal\r\n"
+ "PlayResX: %d\r\n"
+ "PlayResY: %d\r\n"
+ "Timer: 100.0\r\n"
+ "WrapStyle: 0\r\n"
+ "\r\n"
+ "[V4+ Styles]\r\n"
+ "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\r\n"
+ "Style: Default,Arial,%d,&H00FFFFFF,&H00FFFFFF,&H000F0F0F,&H000F0F0F,0,0,0,0,100,100,0,0.00,1,2,3,2,20,20,20,0\r\n";
+
+ subtitle->extradata = (uint8_t*)hb_strdup_printf(ssa_header, w, h, fs);
+ if (subtitle->extradata == NULL)
+ {
+ hb_error("hb_subtitle_add_ssa_header: malloc failed");
+ return 0;
+ }
+ subtitle->extradata_size = strlen((char*)subtitle->extradata) + 1;
+
+ return 1;
+}
+
int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, int track)
{
hb_title_t *title = job->title;
@@ -3698,41 +3731,45 @@ int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlec
/* We fail! */
return 0;
}
+
subtitle->config = *subtitlecfg;
subtitle->out_track = hb_list_count(job->list_subtitle) + 1;
hb_list_add(job->list_subtitle, subtitle);
return 1;
}
-int hb_srt_add( const hb_job_t * job,
- const hb_subtitle_config_t * subtitlecfg,
+int hb_srt_add( const hb_job_t * job,
+ const hb_subtitle_config_t * subtitlecfg,
const char *lang )
{
hb_subtitle_t *subtitle;
iso639_lang_t *language = NULL;
- int retval = 0;
subtitle = calloc( 1, sizeof( *subtitle ) );
-
+ if (subtitle == NULL)
+ {
+ hb_error("hb_srt_add: malloc failed");
+ return 0;
+ }
+
subtitle->id = (hb_list_count(job->list_subtitle) << 8) | 0xFF;
subtitle->format = TEXTSUB;
subtitle->source = SRTSUB;
subtitle->codec = WORK_DECSRTSUB;
- language = lang_for_code2( lang );
-
- if( language )
+ language = lang_for_code2(lang);
+ if (language == NULL)
{
+ hb_log("hb_srt_add: unknown language code (%s)", lang);
+ language = lang_for_code2("und");
+ }
+ strcpy(subtitle->lang, language->eng_name);
+ strcpy(subtitle->iso639_2, language->iso639_2);
- strcpy( subtitle->lang, language->eng_name );
- strncpy( subtitle->iso639_2, lang, 4 );
-
- subtitle->config = *subtitlecfg;
+ subtitle->config = *subtitlecfg;
+ hb_list_add(job->list_subtitle, subtitle);
- hb_list_add(job->list_subtitle, subtitle);
- retval = 1;
- }
- return retval;
+ return 1;
}
int hb_subtitle_can_force( int source )
@@ -4062,7 +4099,7 @@ char * hb_strdup_printf( const char * fmt, ... )
if ( str == NULL )
return NULL;
- while (1)
+ while (1)
{
/* Try to print in the allocated space. */
va_start( ap, fmt );
@@ -4204,15 +4241,15 @@ int hb_yuv2rgb(int yuv)
r = 1.164 * (y - 16) + 1.596 * (Cr - 128);
g = 1.164 * (y - 16) - 0.392 * (Cb - 128) - 0.813 * (Cr - 128);
b = 1.164 * (y - 16) + 2.017 * (Cb - 128);
-
+
r = (r < 0) ? 0 : r;
g = (g < 0) ? 0 : g;
b = (b < 0) ? 0 : b;
-
+
r = (r > 255) ? 255 : r;
g = (g > 255) ? 255 : g;
b = (b > 255) ? 255 : b;
-
+
return (r << 16) | (g << 8) | b;
}
@@ -4230,7 +4267,7 @@ int hb_rgb2yuv(int rgb)
{
double r, g, b;
int y, Cr, Cb;
-
+
r = (rgb >> 16) & 0xff;
g = (rgb >> 8) & 0xff;
b = (rgb ) & 0xff;
@@ -4238,15 +4275,15 @@ int hb_rgb2yuv(int rgb)
y = 16. + ( 0.257 * r) + (0.504 * g) + (0.098 * b);
Cb = 128. + (-0.148 * r) - (0.291 * g) + (0.439 * b);
Cr = 128. + ( 0.439 * r) - (0.368 * g) - (0.071 * b);
-
+
y = (y < 0) ? 0 : y;
Cb = (Cb < 0) ? 0 : Cb;
Cr = (Cr < 0) ? 0 : Cr;
-
+
y = (y > 255) ? 255 : y;
Cb = (Cb > 255) ? 255 : Cb;
Cr = (Cr > 255) ? 255 : Cr;
-
+
return (y << 16) | (Cr << 8) | Cb;
}
@@ -4321,10 +4358,10 @@ void hb_hexdump( hb_debug_level_t level, const char * label, const uint8_t * dat
int hb_gui_use_hwd_flag = 0;
int hb_use_dxva( hb_title_t * title )
{
- return ( (title->video_codec_param == AV_CODEC_ID_MPEG2VIDEO
+ return ( (title->video_codec_param == AV_CODEC_ID_MPEG2VIDEO
|| title->video_codec_param == AV_CODEC_ID_H264
- || title->video_codec_param == AV_CODEC_ID_VC1
- || title->video_codec_param == AV_CODEC_ID_WMV3
+ || title->video_codec_param == AV_CODEC_ID_VC1
+ || title->video_codec_param == AV_CODEC_ID_WMV3
|| title->video_codec_param == AV_CODEC_ID_MPEG4 )
&& title->opaque_priv );
}
diff --git a/libhb/common.h b/libhb/common.h
index 4ccac8402..97ac820df 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -151,6 +151,7 @@ void hb_audio_config_init(hb_audio_config_t * audiocfg);
int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg);
hb_audio_config_t * hb_list_audio_config_item(hb_list_t * list, int i);
+int hb_subtitle_add_ssa_header(hb_subtitle_t *subtitle, int width, int height);
hb_subtitle_t *hb_subtitle_copy(const hb_subtitle_t *src);
hb_list_t *hb_subtitle_list_copy(const hb_list_t *src);
void hb_subtitle_close( hb_subtitle_t **sub );
diff --git a/libhb/deccc608sub.c b/libhb/deccc608sub.c
index 85b43efab..5f4066b67 100644
--- a/libhb/deccc608sub.c
+++ b/libhb/deccc608sub.c
@@ -7,15 +7,12 @@
#include "hb.h"
#include "deccc608sub.h"
+#define SSA_PREAMBLE_LEN 24
/*
* ccextractor static configuration variables.
*/
static int debug_608 = 0;
-static int trim_subs = 1;
-static int nofontcolor = 0;
-static enum encoding_type encoding = ENC_UTF_8;
static int cc_channel = 1;
-static int sentence_cap = 0;
static int subs_delay = 0;
static int norollup = 0;
static int direct_rollup = 0;
@@ -23,28 +20,21 @@ static int direct_rollup = 0;
/*
* Get the time of the last buffer that we have received.
*/
-static int64_t get_fts(struct s_write *wb)
+static int64_t get_last_pts(struct s_write *wb)
{
return wb->last_pts;
}
#define fatal(N, ...) // N
-int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10};
+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 str[2048]; // Another generic general purpose buffer
-
-#define GUARANTEE(wb, length) if (length>wb->enc_buffer_capacity) \
-{wb->enc_buffer_capacity*=2; wb->enc_buffer=(unsigned char*) realloc (wb->enc_buffer, wb->enc_buffer_capacity); \
- if (wb->enc_buffer==NULL) { fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory, bailing out\n"); } \
-}
-
-const unsigned char pac2_attribs[][3]= // Color, font, ident
+static const unsigned char pac2_attribs[][3]= // Color, font, ident
{
{COL_WHITE, FONT_REGULAR, 0}, // 0x40 || 0x60
{COL_WHITE, FONT_UNDERLINED, 0}, // 0x41 || 0x61
@@ -80,17 +70,10 @@ const unsigned char pac2_attribs[][3]= // Color, font, ident
{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;
+static enum color_code default_color=COL_WHITE;
-const char *command_type[] =
+static const char *command_type[] =
{
"Unknown",
"EDM - EraseDisplayedMemory",
@@ -108,7 +91,7 @@ const char *command_type[] =
"RTD - Resume Text Display"
};
-const char *font_text[]=
+static const char *font_text[]=
{
"regular",
"italics",
@@ -116,24 +99,19 @@ const char *font_text[]=
"underlined italics"
};
-const char *cc_modes_text[]=
-{
- "Pop-Up captions"
-};
-
-const char *color_text[][2]=
+static 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=\""}
+ {"white", "&HFFFFFF&"},
+ {"green", "&H00FF00&"},
+ {"blue", "&HFF0000&"},
+ {"cyan", "&HFFFF00&"},
+ {"red", "&H0000FF&"},
+ {"yellow", "&H00FFFF&"},
+ {"magenta", "&HFF00FF&"},
+ {"userdefined", "&HFFFFFF&"}
};
-int general_608_init (struct s_write *wb)
+static int general_608_init (struct s_write *wb)
{
if( !wb->enc_buffer )
{
@@ -167,7 +145,7 @@ int general_608_init (struct s_write *wb)
* 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)
+static void general_608_close (struct s_write *wb)
{
if( wb->enc_buffer ) {
free(wb->enc_buffer);
@@ -186,1076 +164,501 @@ void general_608_close (struct s_write *wb)
#include <ctype.h>
-void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
+// Returns number of bytes used
+static int get_char_in_utf8(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==0x00)
+ if (c == 0x00)
return 0;
- if (c<0x80) // Regular line-21 character set, mostly ASCII except these exceptions
+
+ // Regular line-21 character set, mostly ASCII except these exceptions
+ if (c < 0x80)
{
switch (c)
{
case 0x2a: // lowercase a, acute accent
- *buffer=0xc3;
- *(buffer+1)=0xa1;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa1;
return 2;
case 0x5c: // lowercase e, acute accent
- *buffer=0xc3;
- *(buffer+1)=0xa9;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa9;
return 2;
case 0x5e: // lowercase i, acute accent
- *buffer=0xc3;
- *(buffer+1)=0xad;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xad;
return 2;
case 0x5f: // lowercase o, acute accent
- *buffer=0xc3;
- *(buffer+1)=0xb3;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb3;
return 2;
case 0x60: // lowercase u, acute accent
- *buffer=0xc3;
- *(buffer+1)=0xba;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xba;
return 2;
case 0x7b: // lowercase c with cedilla
- *buffer=0xc3;
- *(buffer+1)=0xa7;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa7;
return 2;
case 0x7c: // division symbol
- *buffer=0xc3;
- *(buffer+1)=0xb7;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb7;
return 2;
case 0x7d: // uppercase N tilde
- *buffer=0xc3;
- *(buffer+1)=0x91;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x91;
return 2;
case 0x7e: // lowercase n tilde
- *buffer=0xc3;
- *(buffer+1)=0xb1;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb1;
return 2;
default:
- *buffer=c;
+ *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
+ // THAT COME FROM HI BYTE = 0x11 AND LOW BETWEEN 0x30 AND 0x3F
case 0x80: // Registered symbol (R)
- *buffer=0xc2;
- *(buffer+1)=0xae;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xae;
return 2;
case 0x81: // degree sign
- *buffer=0xc2;
- *(buffer+1)=0xb0;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xb0;
return 2;
case 0x82: // 1/2 symbol
- *buffer=0xc2;
- *(buffer+1)=0xbd;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xbd;
return 2;
case 0x83: // Inverted (open) question mark
- *buffer=0xc2;
- *(buffer+1)=0xbf;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xbf;
return 2;
case 0x84: // Trademark symbol (TM)
- *buffer=0xe2;
- *(buffer+1)=0x84;
- *(buffer+2)=0xa2;
+ *buffer = 0xe2;
+ *(buffer+1) = 0x84;
+ *(buffer+2) = 0xa2;
return 3;
case 0x85: // Cents symbol
- *buffer=0xc2;
- *(buffer+1)=0xa2;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xa2;
return 2;
case 0x86: // Pounds sterling
- *buffer=0xc2;
- *(buffer+1)=0xa3;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xa3;
return 2;
case 0x87: // Music note
- *buffer=0xe2;
- *(buffer+1)=0x99;
- *(buffer+2)=0xaa;
+ *buffer = 0xe2;
+ *(buffer+1) = 0x99;
+ *(buffer+2) = 0xaa;
return 3;
case 0x88: // lowercase a, grave accent
- *buffer=0xc3;
- *(buffer+1)=0xa0;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa0;
return 2;
case 0x89: // transparent space, we make it regular
- *buffer=0x20;
+ *buffer = 0x20;
return 1;
case 0x8a: // lowercase e, grave accent
- *buffer=0xc3;
- *(buffer+1)=0xa8;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa8;
return 2;
case 0x8b: // lowercase a, circumflex accent
- *buffer=0xc3;
- *(buffer+1)=0xa2;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa2;
return 2;
case 0x8c: // lowercase e, circumflex accent
- *buffer=0xc3;
- *(buffer+1)=0xaa;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xaa;
return 2;
case 0x8d: // lowercase i, circumflex accent
- *buffer=0xc3;
- *(buffer+1)=0xae;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xae;
return 2;
case 0x8e: // lowercase o, circumflex accent
- *buffer=0xc3;
- *(buffer+1)=0xb4;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb4;
return 2;
case 0x8f: // lowercase u, circumflex accent
- *buffer=0xc3;
- *(buffer+1)=0xbb;
+ *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
+ // THAT COME FROM HI BYTE = 0x12 AND LOW BETWEEN 0x20 AND 0x3F
case 0x90: // capital letter A with acute
- *buffer=0xc3;
- *(buffer+1)=0x81;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x81;
return 2;
case 0x91: // capital letter E with acute
- *buffer=0xc3;
- *(buffer+1)=0x89;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x89;
return 2;
case 0x92: // capital letter O with acute
- *buffer=0xc3;
- *(buffer+1)=0x93;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x93;
return 2;
case 0x93: // capital letter U with acute
- *buffer=0xc3;
- *(buffer+1)=0x9a;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x9a;
return 2;
case 0x94: // capital letter U with diaresis
- *buffer=0xc3;
- *(buffer+1)=0x9c;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x9c;
return 2;
case 0x95: // lowercase letter U with diaeresis
- *buffer=0xc3;
- *(buffer+1)=0xbc;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xbc;
return 2;
case 0x96: // apostrophe
- *buffer=0x27;
+ *buffer = 0x27;
return 1;
case 0x97: // inverted exclamation mark
- *buffer=0xc2;
- *(buffer+1)=0xa1;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xa1;
return 2;
case 0x98: // asterisk
- *buffer=0x2a;
+ *buffer = 0x2a;
return 1;
case 0x99: // apostrophe (yes, duped). See CCADI source code.
- *buffer=0x27;
+ *buffer = 0x27;
return 1;
case 0x9a: // hyphen-minus
- *buffer=0x2d;
+ *buffer = 0x2d;
return 1;
case 0x9b: // copyright sign
- *buffer=0xc2;
- *(buffer+1)=0xa9;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xa9;
return 2;
case 0x9c: // Service mark
- *buffer=0xe2;
- *(buffer+1)=0x84;
- *(buffer+2)=0xa0;
+ *buffer = 0xe2;
+ *(buffer+1) = 0x84;
+ *(buffer+2) = 0xa0;
return 3;
case 0x9d: // Full stop (.)
- *buffer=0x2e;
+ *buffer = 0x2e;
return 1;
case 0x9e: // Quoatation mark
- *buffer=0x22;
+ *buffer = 0x22;
return 1;
case 0x9f: // Quoatation mark
- *buffer=0x22;
+ *buffer = 0x22;
return 1;
case 0xa0: // uppercase A, grave accent
- *buffer=0xc3;
- *(buffer+1)=0x80;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x80;
return 2;
case 0xa1: // uppercase A, circumflex
- *buffer=0xc3;
- *(buffer+1)=0x82;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x82;
return 2;
case 0xa2: // uppercase C with cedilla
- *buffer=0xc3;
- *(buffer+1)=0x87;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x87;
return 2;
case 0xa3: // uppercase E, grave accent
- *buffer=0xc3;
- *(buffer+1)=0x88;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x88;
return 2;
case 0xa4: // uppercase E, circumflex
- *buffer=0xc3;
- *(buffer+1)=0x8a;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x8a;
return 2;
case 0xa5: // capital letter E with diaresis
- *buffer=0xc3;
- *(buffer+1)=0x8b;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x8b;
return 2;
case 0xa6: // lowercase letter e with diaresis
- *buffer=0xc3;
- *(buffer+1)=0xab;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xab;
return 2;
case 0xa7: // uppercase I, circumflex
- *buffer=0xc3;
- *(buffer+1)=0x8e;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x8e;
return 2;
case 0xa8: // uppercase I, with diaresis
- *buffer=0xc3;
- *(buffer+1)=0x8f;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x8f;
return 2;
case 0xa9: // lowercase i, with diaresis
- *buffer=0xc3;
- *(buffer+1)=0xaf;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xaf;
return 2;
case 0xaa: // uppercase O, circumflex
- *buffer=0xc3;
- *(buffer+1)=0x94;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x94;
return 2;
case 0xab: // uppercase U, grave accent
- *buffer=0xc3;
- *(buffer+1)=0x99;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x99;
return 2;
case 0xac: // lowercase u, grave accent
- *buffer=0xc3;
- *(buffer+1)=0xb9;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb9;
return 2;
case 0xad: // uppercase U, circumflex
- *buffer=0xc3;
- *(buffer+1)=0x9b;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x9b;
return 2;
case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- *buffer=0xc2;
- *(buffer+1)=0xab;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xab;
return 2;
case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- *buffer=0xc2;
- *(buffer+1)=0xbb;
+ *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
+ // THAT COME FROM HI BYTE = 0x13 AND LOW BETWEEN 0x20 AND 0x3F
case 0xb0: // Uppercase A, tilde
- *buffer=0xc3;
- *(buffer+1)=0x83;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x83;
return 2;
case 0xb1: // Lowercase a, tilde
- *buffer=0xc3;
- *(buffer+1)=0xa3;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa3;
return 2;
case 0xb2: // Uppercase I, acute accent
- *buffer=0xc3;
- *(buffer+1)=0x8d;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x8d;
return 2;
case 0xb3: // Uppercase I, grave accent
- *buffer=0xc3;
- *(buffer+1)=0x8c;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x8c;
return 2;
case 0xb4: // Lowercase i, grave accent
- *buffer=0xc3;
- *(buffer+1)=0xac;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xac;
return 2;
case 0xb5: // Uppercase O, grave accent
- *buffer=0xc3;
- *(buffer+1)=0x92;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x92;
return 2;
case 0xb6: // Lowercase o, grave accent
- *buffer=0xc3;
- *(buffer+1)=0xb2;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb2;
return 2;
case 0xb7: // Uppercase O, tilde
- *buffer=0xc3;
- *(buffer+1)=0x95;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x95;
return 2;
case 0xb8: // Lowercase o, tilde
- *buffer=0xc3;
- *(buffer+1)=0xb5;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb5;
return 2;
case 0xb9: // Open curly brace
- *buffer=0x7b;
+ *buffer = 0x7b;
return 1;
case 0xba: // Closing curly brace
- *buffer=0x7d;
+ *buffer = 0x7d;
return 1;
case 0xbb: // Backslash
- *buffer=0x5c;
+ *buffer = 0x5c;
return 1;
case 0xbc: // Caret
- *buffer=0x5e;
+ *buffer = 0x5e;
return 1;
case 0xbd: // Underscore
- *buffer=0x5f;
+ *buffer = 0x5f;
return 1;
case 0xbe: // Pipe (broken bar)
- *buffer=0xc2;
- *(buffer+1)=0xa6;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xa6;
return 1;
case 0xbf: // Tilde
- *buffer=0x7e; // Not sure
+ *buffer = 0x7e; // Not sure
return 1;
case 0xc0: // Uppercase A, umlaut
- *buffer=0xc3;
- *(buffer+1)=0x84;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x84;
return 2;
case 0xc1: // Lowercase A, umlaut
- *buffer=0xc3;
- *(buffer+1)=0xa4;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa4;
return 2;
case 0xc2: // Uppercase O, umlaut
- *buffer=0xc3;
- *(buffer+1)=0x96;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x96;
return 2;
case 0xc3: // Lowercase o, umlaut
- *buffer=0xc3;
- *(buffer+1)=0xb6;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb6;
return 2;
case 0xc4: // Esszett (sharp S)
- *buffer=0xc3;
- *(buffer+1)=0x9f;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x9f;
return 2;
case 0xc5: // Yen symbol
- *buffer=0xc2;
- *(buffer+1)=0xa5;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xa5;
return 2;
case 0xc6: // Currency symbol
- *buffer=0xc2;
- *(buffer+1)=0xa4;
+ *buffer = 0xc2;
+ *(buffer+1) = 0xa4;
return 2;
case 0xc7: // Vertical bar
- *buffer=0x7c;
+ *buffer = 0x7c;
return 1;
case 0xc8: // Uppercase A, ring
- *buffer=0xc3;
- *(buffer+1)=0x85;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x85;
return 2;
case 0xc9: // Lowercase A, ring
- *buffer=0xc3;
- *(buffer+1)=0xa5;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xa5;
return 2;
case 0xca: // Uppercase O, slash
- *buffer=0xc3;
- *(buffer+1)=0x98;
+ *buffer = 0xc3;
+ *(buffer+1) = 0x98;
return 2;
case 0xcb: // Lowercase o, slash
- *buffer=0xc3;
- *(buffer+1)=0xb8;
+ *buffer = 0xc3;
+ *(buffer+1) = 0xb8;
return 2;
case 0xcc: // Upper left corner
- *buffer=0xe2;
- *(buffer+1)=0x8c;
- *(buffer+2)=0x9c;
+ *buffer = 0xe2;
+ *(buffer+1) = 0x8c;
+ *(buffer+2) = 0x9c;
return 3;
case 0xcd: // Upper right corner
- *buffer=0xe2;
- *(buffer+1)=0x8c;
- *(buffer+2)=0x9d;
+ *buffer = 0xe2;
+ *(buffer+1) = 0x8c;
+ *(buffer+2) = 0x9d;
return 3;
case 0xce: // Lower left corner
- *buffer=0xe2;
- *(buffer+1)=0x8c;
- *(buffer+2)=0x9e;
+ *buffer = 0xe2;
+ *(buffer+1) = 0x8c;
+ *(buffer+2) = 0x9e;
return 3;
case 0xcf: // Lower right corner
- *buffer=0xe2;
- *(buffer+1)=0x8c;
- *(buffer+2)=0x9f;
+ *buffer = 0xe2;
+ *(buffer+1) = 0x8c;
+ *(buffer+2) = 0x9f;
return 3;
default: //
- *buffer='?'; // I'll do it eventually, I promise
+ *buffer = '?'; // I'll do it eventually, I promise
return 1; // This are weird chars anyway
}
}
-unsigned char cctolower (unsigned char 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.
+static unsigned encode_line(unsigned char *buffer, unsigned char *text)
{
- 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;
+ 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++;
+ *buffer++ = *text++;
+ bytes++;
}
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)
+static void find_limit_characters(unsigned char *line, int *first_non_blank,
+ int *last_non_blank)
{
int i;
- for (i=0;i<CC608_SCREEN_WIDTH;i++)
+ *last_non_blank = -1;
+ *first_non_blank = -1;
+ for (i = 0; i < CC608_SCREEN_WIDTH; i++)
{
- switch (data->characters[line_num][i])
+ unsigned char c = line[i];
+ if (c != ' ' && c != 0x89)
{
- 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;
+ if (*first_non_blank == -1)
+ *first_non_blank = i;
+ *last_non_blank = i;
}
}
}
-void find_limit_characters (unsigned char *line, int *first_non_blank, int *last_non_blank)
+static unsigned get_decoder_line_encoded(unsigned char *buffer, int line_num,
+ struct eia608_screen *data)
{
+ uint8_t font_style;
+ uint8_t prev_font_style = FONT_REGULAR;
+ uint8_t font_color;
+ uint8_t prev_font_color = COL_WHITE;
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);
+ unsigned char *orig = buffer; // Keep for debugging
+ int first = 0, last = 31;
- if (first_non_blank==-1)
+ find_limit_characters(line, &first, &last);
+ for (i = first; i <= last; i++)
{
- *buffer=0;
- return 0;
- }
+ // Handle color
+ font_color = data->colors[line_num][i];
+ font_style = data->fonts[line_num][i];
- int bytes=0;
- for (i=first_non_blank;i<=last_non_blank;i++)
- {
- char c=line[i];
- switch (encoding)
+ // Handle reset to defaults
+ if ((font_style & FONT_STYLE_MASK) == 0 && font_color == COL_WHITE)
{
- 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;
+ if (((font_style ^ prev_font_style) & FONT_STYLE_MASK) ||
+ (font_color != prev_font_color))
+ {
+ buffer += encode_line(buffer, (uint8_t*)"{\\r}");
+ }
}
- buffer+=bytes;
- }
- *buffer=0;
- return (unsigned) (buffer-orig); // Return length
-}
+ else
+ {
+ // Open markup
+ if (((font_style ^ prev_font_style) & FONT_STYLE_MASK) ||
+ (font_color != prev_font_color))
+ {
+ // style changed
+ buffer += encode_line(buffer, (uint8_t*)"{");
+ }
-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;
+ // Handle underlined
+ if ((font_style ^ prev_font_style) & FONT_UNDERLINED)
+ {
+ int enable = !!(font_style & FONT_UNDERLINED);
+ buffer += encode_line(buffer, (uint8_t*)"\\u");
+ *buffer++ = enable + 0x30;
+ }
- 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
+ // Handle italics
+ if ((font_style ^ prev_font_style) & FONT_ITALICS)
{
- buffer+= encode_line (buffer,(unsigned char *) "</font>");
+ int enable = !!(font_style & FONT_ITALICS);
+ buffer += encode_line(buffer, (uint8_t*)"\\i");
+ *buffer++ = enable + 0x30;
}
- // Add new font tag
- buffer+=encode_line (buffer, (unsigned char*) color_text[its_col][1]);
- if (its_col==COL_USERDEFINED)
+
+ // Handle color
+ if (font_color != prev_font_color)
{
- // 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*) "\">");
+ buffer += encode_line(buffer, (uint8_t*)"\\1c");
+ buffer += encode_line(buffer,
+ (uint8_t*)color_text[font_color][1]);
}
- 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;
+ // Close markup
+ if (((font_style ^ prev_font_style) & FONT_STYLE_MASK) ||
+ (font_color != prev_font_color))
+ {
+ // style changed
+ buffer += encode_line(buffer, (uint8_t*)"}");
+ }
}
- 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>");
+ prev_font_style = font_style;
+ prev_font_color = font_color;
+
+ int bytes = 0;
+ bytes = get_char_in_utf8(buffer, line[i]);
+ buffer += bytes;
}
- *buffer=0;
- return (unsigned) (buffer-orig); // Return length
+ *buffer = 0;
+ return (unsigned) (buffer - orig); // Return length
}
-void delete_all_lines_but_current (struct eia608_screen *data, int row)
+static void delete_all_lines_but_current (struct eia608_screen *data, int row)
{
int i;
for (i=0;i<15;i++)
@@ -1271,7 +674,7 @@ void delete_all_lines_but_current (struct eia608_screen *data, int row)
}
}
-void clear_eia608_cc_buffer (struct eia608_screen *data)
+static void clear_eia608_cc_buffer (struct eia608_screen *data)
{
int i;
@@ -1286,27 +689,26 @@ void clear_eia608_cc_buffer (struct eia608_screen *data)
data->empty=1;
}
-void init_eia608 (struct eia608 *data)
+static void init_eia608 (struct eia608 *data)
{
- data->cursor_column=0;
- data->cursor_row=0;
+ 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;
+ data->visible_buffer = 1;
+ data->last_c1 = 0;
+ data->last_c2 = 0;
+ data->mode = MODE_POPUP;
+ data->current_visible_start_ms = 0;
+ data->ssa_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_current_hidden_buffer(struct s_write *wb)
+static struct eia608_screen *get_current_hidden_buffer(struct s_write *wb)
{
struct eia608_screen *data;
if (wb->data608->visible_buffer == 1)
@@ -1316,7 +718,7 @@ struct eia608_screen *get_current_hidden_buffer(struct s_write *wb)
return data;
}
-struct eia608_screen *get_current_visible_buffer(struct s_write *wb)
+static struct eia608_screen *get_current_visible_buffer(struct s_write *wb)
{
struct eia608_screen *data;
if (wb->data608->visible_buffer == 1)
@@ -1331,7 +733,7 @@ static void swap_visible_buffer(struct s_write *wb)
wb->data608->visible_buffer = (wb->data608->visible_buffer == 1) ? 2 : 1;
}
-struct eia608_screen *get_writing_buffer (struct s_write *wb)
+static struct eia608_screen *get_writing_buffer(struct s_write *wb)
{
struct eia608_screen *use_buffer=NULL;
switch (wb->data608->mode)
@@ -1350,27 +752,28 @@ struct eia608_screen *get_writing_buffer (struct s_write *wb)
return use_buffer;
}
-void write_char (const unsigned char c, struct s_write *wb)
+static void write_char(const unsigned char c, struct s_write *wb)
{
- if (wb->data608->mode!=MODE_TEXT)
+ if (wb->data608->mode != MODE_TEXT)
{
- struct eia608_screen * use_buffer=get_writing_buffer(wb);
+ 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)
+ 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)
+static 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;
@@ -1389,56 +792,34 @@ void handle_text_attr (const unsigned char c1, const unsigned char c2, struct s_
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]);
+ 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 (int64_t milli, unsigned *hours, unsigned *minutes,
- unsigned *seconds, unsigned *ms)
+static int write_cc_buffer_as_ssa(struct eia608_screen *data,
+ struct s_write *wb)
{
- // int64_t milli = (int64_t) ((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 fhb_log_encoded (FILE *fh, const char *string)
-{
- //GUARANTEE(wb, strlen (string)*3);
- //wb->enc_buffer_used=encode_line (wb->enc_buffer,(unsigned char *) string);
- //fwrite (wb->enc_buffer,wb->enc_buffer_used,1,fh);
-}
-
-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;
- int64_t ms_start= wb->data608->current_visible_start_ms;
int i;
+ int64_t ms_start = wb->data608->current_visible_start_ms;
+ //int64_t ms_end = get_last_pts(wb) + subs_delay;
+ int row = -1, col = -1;
- ms_start+=subs_delay;
+ ms_start += subs_delay;
if (ms_start<0) // Drop screens that because of subs_delay start too early
return 0;
- int64_t ms_end = get_fts(wb)+subs_delay;
- mstotime (ms_start,&h1,&m1,&s1,&ms1);
- mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
- char timeline[128];
- wb->data608->srt_counter++;
- sprintf (timeline,"%u\r\n",wb->data608->srt_counter);
-
if (debug_608)
{
- hb_log ("\n- - - SRT caption - - -\n");
+ char timeline[128];
+ wb->data608->ssa_counter++;
+ sprintf (timeline,"%u\r\n",wb->data608->ssa_counter);
+
+ hb_log ("\n- - - SSA caption - - -\n");
hb_log ("%s", timeline);
}
@@ -1447,58 +828,122 @@ int write_cc_buffer_as_srt(struct eia608_screen *data, struct s_write *wb)
* ensure that we only have two lines, insert a newline after the first one,
* and have a big bottom line (strip spaces from any joined lines).
*/
+ int rows = 0, columns = 0;
+ for (i = 0; i < 15; i++)
+ {
+ if (data->row_used[i])
+ {
+ int first, last;
+
+ rows++;
+ find_limit_characters(data->characters[i], &first, &last);
+ if (last - first + 1 > columns)
+ columns = last - first + 1;
+ }
+ }
+
wb->enc_buffer_used = 0;
int line = 1;
- for (i=0;i<15;i++)
+ for (i = 0; i < 15; i++)
{
if (data->row_used[i])
{
- if (sentence_cap)
+ // Get position for this CC
+ if (row == -1)
{
- capitalize(i,data, &wb->new_sentence);
- correct_case(i,data);
+ int last, x, y, safe_zone, cell_width, cell_height;
+ int cropped_width, cropped_height, font_size;
+ char *pos;
+
+ row = i;
+ find_limit_characters(data->characters[i], &col, &last);
+
+ // CC grid is 16 rows by 62 colums
+ // Our SSA resolution is the title resolution
+ // Tranlate CC grid to SSA coordinates
+ // The numbers are tweaked to keep things off the very
+ // edges of the screen and in the "safe" zone
+ cropped_height = wb->height - wb->crop[0] - wb->crop[1];
+ cropped_width = wb->width - wb->crop[2] - wb->crop[3];
+ font_size = cropped_height * .066;
+ safe_zone = wb->height * 0.025;
+ cell_height = (wb->height - 2 * safe_zone) / 16;
+ cell_width = (wb->width - 2 * safe_zone) / 32;
+
+ // Calculate position assuming the position defines
+ // the baseline of the text which is lower left corner
+ // of bottom row of characters
+ y = cell_height * (row + 1 + rows) + safe_zone - wb->crop[0];
+ x = cell_width * col + safe_zone - wb->crop[2];
+ if (y < 0)
+ y = (rows * font_size) + safe_zone;
+ if (x < 0)
+ x = safe_zone;
+ if (y > cropped_height)
+ y = cropped_height - safe_zone;
+ if (x + columns * cell_width > cropped_width)
+ x = cropped_width - columns * cell_width - safe_zone;
+ pos = hb_strdup_printf("{\\a1\\pos(%d,%d)}", x, y);
+ wb->enc_buffer_used += encode_line(
+ wb->enc_buffer + wb->enc_buffer_used, (uint8_t*)pos);
+ free(pos);
}
+
/*
- * The intention was to use a newline but QT doesn't like it, old code still
- * here just in case..
+ * The intention was to use a newline but QT doesn't like it,
+ * old code still here just in case..
*/
if (line == 1) {
- wb->enc_buffer_used = get_decoder_line_encoded(wb->enc_buffer, i, data);
+ wb->enc_buffer_used += get_decoder_line_encoded(
+ wb->enc_buffer + wb->enc_buffer_used, i, data);
line = 2;
} else {
- wb->enc_buffer_used += encode_line(wb->enc_buffer+wb->enc_buffer_used,
- (unsigned char *) "\n");
- wb->enc_buffer_used += get_decoder_line_encoded(wb->enc_buffer+wb->enc_buffer_used, i, data);
+ wb->enc_buffer_used += encode_line(
+ wb->enc_buffer + wb->enc_buffer_used, (uint8_t*)"\\N");
+ wb->enc_buffer_used += get_decoder_line_encoded(
+ wb->enc_buffer + wb->enc_buffer_used, i, data);
}
}
}
- if (wb->enc_buffer_used)
+ if (wb->enc_buffer_used && wb->enc_buffer[0] != 0)
{
- hb_buffer_t *buffer = hb_buffer_init( wb->enc_buffer_used + 1 );
+ hb_buffer_t *buffer;
+ int len;
+
+ // bump past null terminator
+ wb->enc_buffer_used++;
+ buffer = hb_buffer_init(wb->enc_buffer_used + SSA_PREAMBLE_LEN);
buffer->s.frametype = HB_FRAME_SUBTITLE;
buffer->s.start = ms_start;
buffer->s.stop = AV_NOPTS_VALUE;
- memcpy( buffer->data, wb->enc_buffer, wb->enc_buffer_used + 1 );
- if (wb->hb_last_buffer) {
+ sprintf((char*)buffer->data, "%d,,Default,,0,0,0,,", ++wb->line);
+ len = strlen((char*)buffer->data);
+ memcpy(buffer->data + len, wb->enc_buffer, wb->enc_buffer_used);
+ if (wb->hb_last_buffer)
+ {
wb->hb_last_buffer->next = buffer;
- } else {
+ }
+ else
+ {
wb->hb_buffer = buffer;
}
wb->hb_last_buffer = buffer;
-
wrote_something=1;
wb->clear_sub_needed = 1;
}
else if (wb->clear_sub_needed)
{
- hb_buffer_t *buffer = hb_buffer_init( 1 );
+ hb_buffer_t *buffer = hb_buffer_init(1);
buffer->s.frametype = HB_FRAME_SUBTITLE;
buffer->s.start = ms_start;
buffer->s.stop = ms_start;
buffer->data[0] = 0;
- if (wb->hb_last_buffer != NULL) {
+ if (wb->hb_last_buffer != NULL)
+ {
wb->hb_last_buffer->next = buffer;
- } else {
+ }
+ else
+ {
wb->hb_buffer = buffer;
}
wb->hb_last_buffer = buffer;
@@ -1511,18 +956,18 @@ int write_cc_buffer_as_srt(struct eia608_screen *data, struct s_write *wb)
return wrote_something;
}
-int write_cc_buffer(struct s_write *wb)
+static int write_cc_buffer(struct s_write *wb)
{
struct eia608_screen *data;
int wrote_something=0;
data = get_current_visible_buffer(wb);
wb->new_sentence=1;
- wrote_something = write_cc_buffer_as_srt (data, wb);
+ wrote_something = write_cc_buffer_as_ssa(data, wb);
return wrote_something;
}
-void roll_up(struct s_write *wb)
+static void roll_up(struct s_write *wb)
{
struct eia608_screen *use_buffer;
int i, j;
@@ -1532,29 +977,29 @@ void roll_up(struct s_write *wb)
switch (wb->data608->mode)
{
case MODE_ROLLUP_2:
- keep_lines=2;
+ keep_lines = 2;
break;
case MODE_ROLLUP_3:
- keep_lines=3;
+ keep_lines = 3;
break;
case MODE_ROLLUP_4:
- keep_lines=4;
+ keep_lines = 4;
break;
default: // Shouldn't happen
- keep_lines=0;
+ keep_lines = 0;
break;
}
- int firstrow=-1, lastrow=-1;
+ 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++)
+ 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 (firstrow == -1)
+ firstrow = i;
+ lastrow = i;
}
}
@@ -1564,37 +1009,37 @@ void roll_up(struct s_write *wb)
if (lastrow==-1) // Empty screen, nothing to rollup
return;
- for (j=lastrow-keep_lines+1;j<lastrow; j++)
+ for (j = lastrow - keep_lines + 1; j < lastrow; j++)
{
- if (j>=0)
+ 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];
+ 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++)
+ 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[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);
+ 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;
+ 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++)
+ rows_now = 0;
+ for (i = 0; i < 15; i++)
if (use_buffer->row_used[i])
rows_now++;
- if (rows_now>keep_lines)
+ if (rows_now > keep_lines)
hb_log ("Bug in roll_up, should have %d lines but I have %d.\n",
keep_lines, rows_now);
}
@@ -1613,7 +1058,7 @@ void erase_memory (struct s_write *wb, int displayed)
clear_eia608_cc_buffer (buf);
}
-int is_current_row_empty (struct s_write *wb)
+static int is_current_row_empty (struct s_write *wb)
{
struct eia608_screen *use_buffer;
int i;
@@ -1628,7 +1073,8 @@ int is_current_row_empty (struct s_write *wb)
}
/* Process GLOBAL CODES */
-void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct s_write *wb)
+static void handle_command(unsigned char c1, const unsigned char c2,
+ struct s_write *wb)
{
// Handle channel change
wb->data608->channel=wb->new_channel;
@@ -1674,7 +1120,7 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
if (wb->data608->cursor_column>0)
{
wb->data608->cursor_column--;
- get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' ';
+ get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column] = ' ';
}
break;
case COM_TABOFFSET1:
@@ -1693,11 +1139,11 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
break;
case COM_RESUMECAPTIONLOADING:
wb->data608->mode=MODE_POPUP;
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->current_visible_start_ms = get_last_pts(wb);
break;
case COM_RESUMETEXTDISPLAY:
wb->data608->mode=MODE_TEXT;
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->current_visible_start_ms = get_last_pts(wb);
break;
case COM_ROLLUP2:
if (wb->data608->mode==MODE_POPUP)
@@ -1714,11 +1160,11 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
handle_command(0x14, 0x2D, wb);
wb->rollup_cr = 1;
}
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->current_visible_start_ms = get_last_pts(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;
+ wb->data608->cursor_column = 0;
+ wb->data608->cursor_row = wb->data608->rollup_base_row;
break;
case COM_ROLLUP3:
if (wb->data608->mode==MODE_POPUP)
@@ -1734,11 +1180,11 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
handle_command(0x14, 0x2D, wb);
wb->rollup_cr = 1;
}
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->current_visible_start_ms = get_last_pts(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;
+ wb->data608->cursor_column = 0;
+ wb->data608->cursor_row = wb->data608->rollup_base_row;
break;
case COM_ROLLUP4:
if (wb->data608->mode==MODE_POPUP)
@@ -1754,10 +1200,10 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
handle_command(0x14, 0x2D, wb);
wb->rollup_cr = 1;
}
- wb->data608->current_visible_start_ms = get_fts(wb);
- wb->data608->mode=MODE_ROLLUP_4;
- wb->data608->cursor_column=0;
- wb->data608->cursor_row=wb->data608->rollup_base_row;
+ wb->data608->current_visible_start_ms = get_last_pts(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:
@@ -1768,17 +1214,16 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
if (wb->rollup_cr && is_current_row_empty(wb))
{
wb->rollup_cr = 0;
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->current_visible_start_ms = get_last_pts(wb);
break;
}
-
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->cursor_column=0;
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->cursor_column = 0;
+ wb->data608->current_visible_start_ms = get_last_pts(wb);
break;
case COM_ERASENONDISPLAYEDMEMORY:
erase_memory (wb,0);
@@ -1791,7 +1236,7 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
{
// If popup, the last pts is the time to remove the
// popup from the screen
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->current_visible_start_ms = get_last_pts(wb);
}
// Write "clear" subtitle if necessary
write_cc_buffer(wb);
@@ -1802,15 +1247,15 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
if (wb->data608->mode == MODE_POPUP)
{
swap_visible_buffer(wb);
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->current_visible_start_ms = get_last_pts(wb);
}
if (write_cc_buffer(wb))
wb->data608->screenfuls_counter++;
if (wb->data608->mode != MODE_POPUP)
swap_visible_buffer(wb);
- wb->data608->cursor_column=0;
- wb->data608->cursor_row=0;
+ wb->data608->cursor_column = 0;
+ wb->data608->cursor_row = 0;
wb->data608->color=default_color;
wb->data608->font=FONT_REGULAR;
break;
@@ -1823,14 +1268,15 @@ void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct
}
}
-void handle_end_of_data (struct s_write *wb)
+static 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)
+static void handle_double(const unsigned char c1, const unsigned char c2,
+ struct s_write *wb)
{
unsigned char c;
if (wb->data608->channel!=cc_channel)
@@ -1845,7 +1291,8 @@ void handle_double (const unsigned char c1, const unsigned char c2, struct s_wri
}
/* Process EXTENDED CHARACTERS */
-unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_write *wb)
+static unsigned char handle_extended(unsigned char hi, unsigned char lo,
+ struct s_write *wb)
{
// Handle channel change
if (wb->new_channel > 2)
@@ -1886,7 +1333,7 @@ unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_writ
}
/* Process PREAMBLE ACCESS CODES (PAC) */
-void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb)
+static void handle_pac(unsigned char c1, unsigned char c2, struct s_write *wb)
{
// Handle channel change
if (wb->new_channel > 2)
@@ -1926,19 +1373,19 @@ void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb)
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)
+ 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->cursor_row = row - 1 ; // Since the array is 0 based
}
- wb->data608->rollup_base_row=row-1;
- wb->data608->cursor_column=indent;
+ wb->data608->rollup_base_row = row - 1;
+ wb->data608->cursor_column = indent;
}
-void handle_single (const unsigned char c1, struct s_write *wb)
+static 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
@@ -1950,7 +1397,7 @@ void handle_single (const unsigned char c1, struct s_write *wb)
write_char (c1,wb);
}
-int check_channel (unsigned char c1, struct s_write *wb)
+static int check_channel(unsigned char c1, struct s_write *wb)
{
if (c1==0x14)
{
@@ -1984,7 +1431,7 @@ int check_channel (unsigned char c1, struct s_write *wb)
/* 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)
+static int disCommand(unsigned char hi, unsigned char lo, struct s_write *wb)
{
int wrote_to_screen=0;
@@ -2050,7 +1497,8 @@ int disCommand (unsigned char hi, unsigned char lo, struct s_write *wb)
return wrote_to_screen;
}
-void process608 (const unsigned char *data, int length, struct s_write *wb)
+static void process608(const unsigned char *data, int length,
+ struct s_write *wb)
{
static int textprinted = 0;
int i;
@@ -2125,7 +1573,7 @@ void process608 (const unsigned char *data, int length, struct s_write *wb)
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()));
+ //hb_log("Current FTS: %s\n", print_mstime(get_last_pts()));
}
if (wrote_to_screen && direct_rollup && // If direct_rollup is enabled and
@@ -2135,55 +1583,19 @@ void process608 (const unsigned char *data, int length, struct s_write *wb)
{
// We don't increase screenfuls_counter here.
write_cc_buffer(wb);
- wb->data608->current_visible_start_ms = get_fts(wb);
+ wb->data608->current_visible_start_ms = get_last_pts(wb);
}
}
}
}
-
-/* Return a pointer to a string that holds the printable characters
- * of the caption data block. FOR DEBUG PURPOSES ONLY! */
-unsigned char *debug_608toASC (unsigned char *cc_data, int channel)
-{
- static unsigned char output[3];
-
- unsigned char cc_valid = (cc_data[0] & 4) >>2;
- unsigned char cc_type = cc_data[0] & 3;
- unsigned char hi, lo;
-
- output[0]=' ';
- output[1]=' ';
- output[2]='\x00';
-
- if (cc_valid && cc_type==channel)
- {
- hi = cc_data[1] & 0x7F; // Get rid of parity bit
- lo = cc_data[2] & 0x7F; // Get rid of parity bit
- if (hi>=0x20)
- {
- output[0]=hi;
- output[1]=(lo>=20 ? lo : '.');
- output[2]='\x00';
- }
- else
- {
- output[0]='<';
- output[1]='>';
- output[2]='\x00';
- }
- }
- return output;
-}
-
-
struct hb_work_private_s
{
hb_job_t * job;
struct s_write * cc608;
};
-int decccInit( hb_work_object_t * w, hb_job_t * job )
+static int decccInit( hb_work_object_t * w, hb_job_t * job )
{
int retval = 1;
hb_work_private_t * pv;
@@ -2199,6 +1611,9 @@ int decccInit( hb_work_object_t * w, hb_job_t * job )
if( pv->cc608 )
{
+ pv->cc608->width = job->title->width;
+ pv->cc608->height = job->title->height;
+ memcpy(pv->cc608->crop, job->crop, sizeof(int[4]));
retval = general_608_init(pv->cc608);
if( !retval )
{
@@ -2211,10 +1626,17 @@ int decccInit( hb_work_object_t * w, hb_job_t * job )
}
}
}
+ if (!retval)
+ {
+ // Generate generic SSA Script Info.
+ int height = job->title->height - job->crop[0] - job->crop[1];
+ int width = job->title->width - job->crop[2] - job->crop[3];
+ hb_subtitle_add_ssa_header(w->subtitle, width, height);
+ }
return retval;
}
-int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+static int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
@@ -2254,7 +1676,7 @@ int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
return HB_WORK_OK;
}
-void decccClose( hb_work_object_t * w )
+static void decccClose( hb_work_object_t * w )
{
hb_work_private_t * pv = w->private_data;
general_608_close( pv->cc608 );
diff --git a/libhb/deccc608sub.h b/libhb/deccc608sub.h
index baf7ca081..3b000f3f2 100644
--- a/libhb/deccc608sub.h
+++ b/libhb/deccc608sub.h
@@ -1,26 +1,13 @@
/*
- * 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.
+ * From ccextractor...
*/
-#ifndef __deccc608sub_H__
-#define __deccc608sub_H__
+#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
@@ -29,7 +16,7 @@ enum cc_modes
MODE_ROLLUP_2 = 1,
MODE_ROLLUP_3 = 2,
MODE_ROLLUP_4 = 3,
- MODE_TEXT = 4
+ MODE_TEXT = 4
};
enum color_code
@@ -41,7 +28,7 @@ enum color_code
COL_RED = 4,
COL_YELLOW = 5,
COL_MAGENTA = 6,
- COL_USERDEFINED = 7
+ COL_USERDEFINED = 7
};
@@ -53,26 +40,26 @@ enum font_bits
FONT_UNDERLINED_ITALICS = 3
};
+#define FONT_STYLE_MASK FONT_UNDERLINED_ITALICS
struct eia608_screen // A CC buffer
{
- unsigned char characters[15][33];
+ 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?
+ int empty; // Buffer completely empty?
};
struct eia608
{
struct eia608_screen buffer1;
- struct eia608_screen buffer2;
+ struct eia608_screen buffer2;
int cursor_row, cursor_column;
int visible_buffer;
- int srt_counter; // Number of subs currently written
+ int ssa_counter; // Number of subs currently written
int screenfuls_counter; // Number of meaningful screenfuls written
int64_t 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
@@ -84,7 +71,7 @@ struct eia608
struct s_write {
struct eia608 *data608;
FILE *fh;
- unsigned char *subline;
+ unsigned char *subline;
int new_sentence;
int new_channel;
int in_xds_mode;
@@ -97,7 +84,12 @@ struct s_write {
int clear_sub_needed; // Indicates that we need to send a null
// subtitle to clear the current subtitle
+
int rollup_cr; // Flag indicates if CR command performed by rollup
+ int line; // SSA line number
+ int width;
+ int height;
+ int crop[4];
};
enum command_code
@@ -115,7 +107,7 @@ enum command_code
COM_CARRIAGERETURN = 10,
COM_ERASENONDISPLAYEDMEMORY = 11,
COM_BACKSPACE = 12,
- COM_RESUMETEXTDISPLAY = 13
+ COM_RESUMETEXTDISPLAY = 13
};
enum encoding_type
@@ -127,11 +119,11 @@ enum encoding_type
enum output_format
{
- OF_RAW = 0,
- OF_SRT = 1,
+ OF_RAW = 0,
+ OF_SRT = 1,
OF_SAMI = 2,
OF_TRANSCRIPT = 3,
OF_RCWT = 4
};
-#endif
+#endif // __DECCC608SUB_H__
diff --git a/libhb/decsrtsub.c b/libhb/decsrtsub.c
index d4e74fdae..5854138a3 100644
--- a/libhb/decsrtsub.c
+++ b/libhb/decsrtsub.c
@@ -13,6 +13,8 @@
#include <iconv.h>
#include <errno.h>
#include "hb.h"
+#include "colormap.h"
+#include "decsrtsub.h"
struct start_and_end {
unsigned long start, end;
@@ -56,17 +58,152 @@ struct hb_work_private_s
hb_subtitle_t *subtitle;
uint64_t start_time; // In HB time
uint64_t stop_time; // In HB time
+
+ int line; // SSA line number
};
-static int
+static char* srt_markup_to_ssa(char *srt, int *len)
+{
+ char terminator;
+ char color[40];
+ uint32_t rgb;
+
+ *len = 0;
+ if (srt[0] != '<' && srt[0] != '{')
+ return NULL;
+
+ if (srt[0] == '<')
+ terminator = '>';
+ else
+ terminator = '}';
+
+ if (srt[1] == 'i' && srt[2] == terminator)
+ {
+ *len = 3;
+ return hb_strdup_printf("{\\i1}");
+ }
+ else if (srt[1] == 'b' && srt[2] == terminator)
+ {
+ *len = 3;
+ return hb_strdup_printf("{\\b1}");
+ }
+ else if (srt[1] == 'u' && srt[2] == terminator)
+ {
+ *len = 3;
+ return hb_strdup_printf("{\\u1}");
+ }
+ else if (srt[1] == '/' && srt[2] == 'i' && srt[3] == terminator)
+ {
+ *len = 4;
+ return hb_strdup_printf("{\\i0}");
+ }
+ else if (srt[1] == '/' && srt[2] == 'b' && srt[3] == terminator)
+ {
+ *len = 4;
+ return hb_strdup_printf("{\\b0}");
+ }
+ else if (srt[1] == '/' && srt[2] == 'u' && srt[3] == terminator)
+ {
+ *len = 4;
+ return hb_strdup_printf("{\\u0}");
+ }
+ else if (srt[0] == '<' && !strncmp(srt + 1, "font", 4))
+ {
+ int match;
+ match = sscanf(srt + 1, "font color=\"%39[^\"]\">", color);
+ if (match != 1)
+ {
+ return NULL;
+ }
+ while (srt[*len] != '>') (*len)++;
+ (*len)++;
+ if (color[0] == '#')
+ rgb = strtol(color + 1, NULL, 16);
+ else
+ rgb = hb_rgb_lookup_by_name(color);
+ return hb_strdup_printf("{\\1c&H%X&}", HB_RGB_TO_BGR(rgb));
+ }
+ else if (srt[0] == '<' && srt[1] == '/' && !strncmp(srt + 2, "font", 4) &&
+ srt[6] == '>')
+ {
+ *len = 7;
+ return hb_strdup_printf("{\\1c&HFFFFFF&}");
+ }
+
+ return NULL;
+}
+
+void hb_srt_to_ssa(hb_buffer_t *sub_in, int line)
+{
+ if (sub_in->size == 0)
+ return;
+
+ // null terminate input if not already terminated
+ if (sub_in->data[sub_in->size-1] != 0)
+ {
+ hb_buffer_realloc(sub_in, ++sub_in->size);
+ sub_in->data[sub_in->size - 1] = 0;
+ }
+ char * srt = (char*)sub_in->data;
+ // SSA markup expands a little over SRT, so allocate a bit of extra
+ // space. More will be realloc'd if needed.
+ hb_buffer_t * sub = hb_buffer_init(sub_in->size + 80);
+ char * ssa, *ssa_markup;
+ int skip, len, pos, ii;
+
+ // Exchange data between input sub and new ssa_sub
+ // After this, sub_in contains ssa data
+ hb_buffer_swap_copy(sub_in, sub);
+ ssa = (char*)sub_in->data;
+
+ sprintf((char*)sub_in->data, "%d,,Default,,0,0,0,,", line);
+ pos = strlen((char*)sub_in->data);
+
+ ii = 0;
+ while (srt[ii] != '\0')
+ {
+ if ((ssa_markup = srt_markup_to_ssa(srt + ii, &skip)) != NULL)
+ {
+ len = strlen(ssa_markup);
+ hb_buffer_realloc(sub_in, pos + len + 1);
+ // After realloc, sub_in->data may change
+ ssa = (char*)sub_in->data;
+ sprintf(ssa + pos, "%s", ssa_markup);
+ free(ssa_markup);
+ pos += len;
+ ii += skip;
+ }
+ else
+ {
+ hb_buffer_realloc(sub_in, pos + 4);
+ // After realloc, sub_in->data may change
+ ssa = (char*)sub_in->data;
+ if (srt[ii] == '\n')
+ {
+ ssa[pos++] = '\\';
+ ssa[pos++] = 'N';
+ ii++;
+ }
+ else
+ {
+ ssa[pos++] = srt[ii++];
+ }
+ }
+ }
+ ssa[pos] = '\0';
+ sub_in->size = pos + 1;
+ hb_buffer_close(&sub);
+}
+
+static int
read_time_from_string( const char* timeString, struct start_and_end *result )
{
// for ex. 00:00:15,248 --> 00:00:16,545
-
+
long houres1, minutes1, seconds1, milliseconds1,
houres2, minutes2, seconds2, milliseconds2;
int scanned;
-
+
scanned = sscanf(timeString, "%ld:%ld:%ld,%ld --> %ld:%ld:%ld,%ld\n",
&houres1, &minutes1, &seconds1, &milliseconds1,
&houres2, &minutes2, &seconds2, &milliseconds2);
@@ -207,8 +344,8 @@ static hb_buffer_t *srt_read( hb_work_private_t *pv )
{
return NULL;
}
-
- while( reprocess || get_line( pv, line_buffer, sizeof( line_buffer ) ) )
+
+ while( reprocess || get_line( pv, line_buffer, sizeof( line_buffer ) ) )
{
reprocess = 0;
switch (pv->current_state)
@@ -227,7 +364,7 @@ static hb_buffer_t *srt_read( hb_work_private_t *pv )
}
pv->current_entry.duration = timing.end - timing.start;
pv->current_entry.offset = timing.start - pv->current_time;
-
+
pv->current_time = timing.end;
pv->current_entry.start = timing.start;
@@ -276,7 +413,7 @@ static hb_buffer_t *srt_read( hb_work_private_t *pv )
pv->current_state = k_state_potential_new_entry;
continue;
}
-
+
q = pv->current_entry.text + pv->current_entry.pos;
len = strlen( line_buffer );
size = MIN(1024 - pv->current_entry.pos - 1, len );
@@ -329,9 +466,9 @@ static hb_buffer_t *srt_read( hb_work_private_t *pv )
long length;
char *p, *q;
int line = 1;
- uint64_t start_time = ( pv->current_entry.start +
+ uint64_t start_time = ( pv->current_entry.start +
pv->subtitle->config.offset ) * 90;
- uint64_t stop_time = ( pv->current_entry.stop +
+ uint64_t stop_time = ( pv->current_entry.stop +
pv->subtitle->config.offset ) * 90;
if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
@@ -396,7 +533,7 @@ static hb_buffer_t *srt_read( hb_work_private_t *pv )
return buffer;
}
continue;
- }
+ }
}
}
@@ -406,9 +543,9 @@ static hb_buffer_t *srt_read( hb_work_private_t *pv )
long length;
char *p, *q;
int line = 1;
- uint64_t start_time = ( pv->current_entry.start +
+ uint64_t start_time = ( pv->current_entry.start +
pv->subtitle->config.offset ) * 90;
- uint64_t stop_time = ( pv->current_entry.stop +
+ uint64_t stop_time = ( pv->current_entry.stop +
pv->subtitle->config.offset ) * 90;
if( !( start_time > pv->start_time && stop_time < pv->stop_time ) )
@@ -467,7 +604,7 @@ static hb_buffer_t *srt_read( hb_work_private_t *pv )
{
return buffer;
}
-
+
return NULL;
}
@@ -488,7 +625,7 @@ static int decsrtInit( hb_work_object_t * w, hb_job_t * job )
buffer = hb_buffer_init( 0 );
hb_fifo_push( w->fifo_in, buffer);
-
+
pv->current_state = k_state_potential_new_entry;
pv->number_of_entries = 0;
pv->last_entry_number = 0;
@@ -540,14 +677,20 @@ static int decsrtInit( hb_work_object_t * w, hb_job_t * job )
if( !pv->file )
{
- hb_error("Could not open the SRT subtitle file '%s'\n",
+ hb_error("Could not open the SRT subtitle file '%s'\n",
w->subtitle->config.src_filename);
} else {
retval = 0;
}
}
- }
-
+ }
+ if (!retval)
+ {
+ // Generate generic SSA Script Info.
+ int height = job->title->height - job->crop[0] - job->crop[1];
+ int width = job->title->width - job->crop[2] - job->crop[3];
+ hb_subtitle_add_ssa_header(w->subtitle, width, height);
+ }
return retval;
}
@@ -559,9 +702,10 @@ static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t * out = NULL;
out = srt_read( pv );
-
if( out )
{
+ hb_srt_to_ssa(out, ++pv->line);
+
/*
* Keep a buffer in our input fifo so that we get run.
*/
@@ -573,7 +717,7 @@ static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
return HB_WORK_OK;
}
- return HB_WORK_OK;
+ return HB_WORK_OK;
}
static void decsrtClose( hb_work_object_t * w )
diff --git a/libhb/decsrtsub.h b/libhb/decsrtsub.h
new file mode 100644
index 000000000..ff7a311e6
--- /dev/null
+++ b/libhb/decsrtsub.h
@@ -0,0 +1,16 @@
+/* decsrtsub.h
+ *
+ * Copyright (c) 2003-2014 HandBrake Team
+ * This file is part of the HandBrake source code
+ * Homepage: <http://handbrake.fr/>.
+ * It may be used under the terms of the GNU General Public License v2.
+ * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+
+#ifndef __DECSRTSUB_H__
+#define __DECSRTSUB_H__
+
+void hb_srt_to_ssa(hb_buffer_t *sub_in, int line);
+
+#endif // __DECSRTSUB_H__
+
diff --git a/libhb/decssasub.c b/libhb/decssasub.c
index de2c3c672..3cac39934 100644
--- a/libhb/decssasub.c
+++ b/libhb/decssasub.c
@@ -11,42 +11,35 @@
* Converts SSA subtitles to either:
* (1) TEXTSUB format: UTF-8 subtitles with limited HTML-style markup (<b>, <i>, <u>), or
* (2) PICTURESUB format, using libass.
- *
+ *
* SSA format references:
* http://www.matroska.org/technical/specs/subtitles/ssa.html
* http://moodub.free.fr/video/ass-specs.doc
* vlc-1.0.4/modules/codec/subtitles/subsass.c:ParseSSAString
- *
+ *
* libass references:
* libass-0.9.9/ass.h
* vlc-1.0.4/modules/codec/libass.c
- *
+ *
* @author David Foster (davidfstr)
*/
#include <stdlib.h>
#include <stdio.h>
+#include <ctype.h>
#include "hb.h"
#include <ass/ass.h>
+#include "decssasub.h"
+#include "colormap.h"
struct hb_work_private_s
{
// If decoding to PICTURESUB format:
int readOrder;
- int raw;
hb_job_t *job;
};
-typedef enum {
- BOLD = 0x01,
- ITALIC = 0x02,
- UNDERLINE = 0x04
-} StyleSet;
-
-// "<b></b>".len + "<i></i>".len + "<u></u>".len
-#define MAX_OVERHEAD_PER_OVERRIDE (7 * 3)
-
#define SSA_2_HB_TIME(hr,min,sec,centi) \
( 90L * ( hr * 1000L * 60 * 60 +\
min * 1000L * 60 +\
@@ -55,71 +48,189 @@ typedef enum {
#define SSA_VERBOSE_PACKETS 0
-static StyleSet ssa_parse_style_override( uint8_t *pos, StyleSet prevStyles )
+static int ssa_update_style(char *ssa, hb_subtitle_style_t *style)
{
- StyleSet nextStyles = prevStyles;
- for (;;)
+ int pos, end, index;
+
+ if (ssa[0] != '{')
+ return 0;
+
+ pos = 1;
+ while (ssa[pos] != '}' && ssa[pos] != '\0')
{
- // Skip over leading '{' or last '\\'
+ index = -1;
+
+ // Skip any malformed markup junk
+ while (strchr("\\}", ssa[pos]) == NULL) pos++;
pos++;
-
- // Scan for next \code
- while ( *pos != '\\' && *pos != '}' && *pos != '\0' ) pos++;
- if ( *pos != '\\' )
+ // Check for an index that is in some markup (e.g. font color)
+ if (isdigit(ssa[pos]))
+ {
+ index = ssa[pos++] - 0x30;
+ }
+ // Find the end of this markup clause
+ end = pos;
+ while (strchr("\\}", ssa[end]) == NULL) end++;
+ // Handle simple integer valued attributes
+ if (strchr("ibu", ssa[pos]) != NULL && isdigit(ssa[pos+1]))
{
- // End of style override block
- break;
+ int val = strtol(ssa + pos + 1, NULL, 0);
+ switch (ssa[pos])
+ {
+ case 'i':
+ style->flags = (style->flags & ~HB_STYLE_FLAG_ITALIC) |
+ !!val * HB_STYLE_FLAG_ITALIC;
+ break;
+ case 'b':
+ style->flags = (style->flags & ~HB_STYLE_FLAG_BOLD) |
+ !!val * HB_STYLE_FLAG_BOLD;
+ break;
+ case 'u':
+ style->flags = (style->flags & ~HB_STYLE_FLAG_UNDERLINE) |
+ !!val * HB_STYLE_FLAG_UNDERLINE;
+ break;
+ }
}
-
- // If next chars are \[biu][01], interpret it
- if ( strchr("biu", pos[1]) && strchr("01", pos[2]) )
+ if (ssa[pos] == 'c' && ssa[pos+1] == '&' && ssa[pos+2] == 'H')
{
- StyleSet styleID =
- pos[1] == 'b' ? BOLD :
- pos[1] == 'i' ? ITALIC :
- pos[1] == 'u' ? UNDERLINE : 0;
- int enabled = (pos[2] == '1');
-
- if (enabled)
+ // Font color markup
+ char *endptr;
+ uint32_t bgr;
+
+ bgr = strtol(ssa + pos + 3, &endptr, 16);
+ if (*endptr == '&')
{
- nextStyles |= styleID;
+ switch (index)
+ {
+ case -1:
+ case 1:
+ style->fg_rgb = HB_BGR_TO_RGB(bgr);
+ break;
+ case 2:
+ style->alt_rgb = HB_BGR_TO_RGB(bgr);
+ break;
+ case 3:
+ style->ol_rgb = HB_BGR_TO_RGB(bgr);
+ break;
+ case 4:
+ style->bg_rgb = HB_BGR_TO_RGB(bgr);
+ break;
+ default:
+ // Unknown color index, ignore
+ break;
+ }
}
- else
+ }
+ if ((ssa[pos] == 'a' && ssa[pos+1] == '&' && ssa[pos+2] == 'H') ||
+ (!strcmp(ssa+pos, "alpha") && ssa[pos+5] == '&' && ssa[pos+6] == 'H'))
+ {
+ // Font alpha markup
+ char *endptr;
+ uint8_t alpha;
+ int alpha_pos = 3;
+
+ if (ssa[1] == 'l')
+ alpha_pos = 7;
+
+ alpha = strtol(ssa + pos + alpha_pos, &endptr, 16);
+ if (*endptr == '&')
{
- nextStyles &= ~styleID;
+ // SSA alpha is inverted 0 is opaque
+ alpha = 255 - alpha;
+ switch (index)
+ {
+ case -1:
+ case 1:
+ style->fg_alpha = alpha;
+ break;
+ case 2:
+ style->alt_alpha = alpha;
+ break;
+ case 3:
+ style->ol_alpha = alpha;
+ break;
+ case 4:
+ style->bg_alpha = alpha;
+ break;
+ default:
+ // Unknown alpha index, ignore
+ break;
+ }
}
}
+ pos = end;
}
- return nextStyles;
+ if (ssa[pos] == '}')
+ pos++;
+ return pos;
}
-static void ssa_append_html_tags_for_style_change(
- uint8_t **dst, StyleSet prevStyles, StyleSet nextStyles )
+char * hb_ssa_to_text(char *in, int *consumed, hb_subtitle_style_t *style)
{
- #define APPEND(str) { \
- char *src = str; \
- while (*src) { *(*dst)++ = *src++; } \
+ int markup_len = 0;
+ int in_pos = 0;
+ int out_pos = 0;
+ char *out = malloc(strlen(in) + 1); // out will never be longer than in
+
+ for (in_pos = 0; in[in_pos] != '\0'; in_pos++)
+ {
+ if ((markup_len = ssa_update_style(in + in_pos, style)))
+ {
+ *consumed = in_pos + markup_len;
+ out[out_pos++] = '\0';
+ return out;
+ }
+ // Check escape codes
+ if (in[in_pos] == '\\')
+ {
+ in_pos++;
+ switch (in[in_pos])
+ {
+ case '\0':
+ in_pos--;
+ break;
+ case 'N':
+ case 'n':
+ out[out_pos++] = '\n';
+ break;
+ case 'h':
+ out[out_pos++] = ' ';
+ break;
+ default:
+ out[out_pos++] = in[in_pos];
+ break;
+ }
+ }
+ else
+ {
+ out[out_pos++] = in[in_pos];
+ }
}
+ *consumed = in_pos;
+ out[out_pos++] = '\0';
+ return out;
+}
+
+void hb_ssa_style_init(hb_subtitle_style_t *style)
+{
+ style->flags = 0;
+
+ style->fg_rgb = 0x00FFFFFF;
+ style->alt_rgb = 0x00FFFFFF;
+ style->ol_rgb = 0x000F0F0F;
+ style->bg_rgb = 0x000F0F0F;
- // Reverse-order close all previous styles
- if (prevStyles & UNDERLINE) APPEND("</u>");
- if (prevStyles & ITALIC) APPEND("</i>");
- if (prevStyles & BOLD) APPEND("</b>");
-
- // Forward-order open all next styles
- if (nextStyles & BOLD) APPEND("<b>");
- if (nextStyles & ITALIC) APPEND("<i>");
- if (nextStyles & UNDERLINE) APPEND("<u>");
-
- #undef APPEND
+ style->fg_alpha = 0xFF;
+ style->alt_alpha = 0xFF;
+ style->ol_alpha = 0xFF;
+ style->bg_alpha = 0xFF;
}
-static hb_buffer_t *ssa_decode_line_to_utf8( uint8_t *in_data, int in_size, int in_sequence );
static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence );
/*
* Decodes a single SSA packet to one or more TEXTSUB or PICTURESUB subtitle packets.
- *
+ *
* SSA packet format:
* ( Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text CR LF ) +
* 1 2 3 4 5 6 7 8 9 10
@@ -129,7 +240,7 @@ static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in )
// Store NULL after the end of the buffer to make using string processing safe
hb_buffer_realloc(in, ++in->size);
in->data[in->size - 1] = '\0';
-
+
hb_buffer_t *out_list = NULL;
hb_buffer_t **nextPtr = &out_list;
@@ -142,53 +253,31 @@ static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in )
// Skip empty lines and spaces between adjacent CR and LF
if (curLine[0] == '\0')
continue;
-
+
// Decode an individual SSA line
hb_buffer_t *out;
- if ( w->subtitle->config.dest == PASSTHRUSUB ) {
- out = ssa_decode_line_to_utf8( (uint8_t *) curLine, strlen( curLine ), in->sequence );
- if ( out == NULL )
- continue;
-
- // We shouldn't be storing the extra NULL character,
- // but the MP4 muxer expects this, unfortunately.
- if (out->size > 0 && out->data[out->size - 1] != '\0')
- {
- hb_buffer_realloc(out, ++out->size);
- out->data[out->size - 1] = '\0';
- }
-
- // If the input packet was non-empty, do not pass through
- // an empty output packet (even if the subtitle was empty),
- // as this would be interpreted as an end-of-stream
- if ( in->size > 0 && out->size == 0 ) {
- hb_buffer_close(&out);
- continue;
- }
- } else if ( w->subtitle->config.dest == RENDERSUB ) {
- out = ssa_decode_line_to_mkv_ssa( w, (uint8_t *) curLine, strlen( curLine ), in->sequence );
- if ( out == NULL )
- continue;
- }
-
+ out = ssa_decode_line_to_mkv_ssa(w, (uint8_t *)curLine, strlen(curLine), in->sequence);
+ if ( out == NULL )
+ continue;
+
// Append 'out' to 'out_list'
*nextPtr = out;
nextPtr = &out->next;
}
- // For point-to-point encoding, when the start time of the stream
+ // For point-to-point encoding, when the start time of the stream
// may be offset, the timestamps of the subtitles must be offset as well.
//
// HACK: Here we are making the assumption that, under normal circumstances,
// the output display time of the first output packet is equal to the
// display time of the input packet.
- //
- // During point-to-point encoding, the display time of the input
+ //
+ // During point-to-point encoding, the display time of the input
// packet will be offset to compensate.
- //
- // Therefore we offset all of the output packets by a slip amount
- // such that first output packet's display time aligns with the
- // input packet's display time. This should give the correct time
+ //
+ // Therefore we offset all of the output packets by a slip amount
+ // such that first output packet's display time aligns with the
+ // input packet's display time. This should give the correct time
// when point-to-point encoding is in effect.
if (out_list && out_list->s.start > in->s.start)
{
@@ -203,13 +292,13 @@ static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in )
out = out->next;
}
}
-
+
return out_list;
}
/*
* Parses the start and stop time from the specified SSA packet.
- *
+ *
* Returns true if parsing failed; false otherwise.
*/
static int parse_timing_from_ssa_packet( char *in_data, int64_t *in_start, int64_t *in_stop )
@@ -223,7 +312,7 @@ static int parse_timing_from_ssa_packet( char *in_data, int64_t *in_start, int64
// format specifier "%*128[^,]" will not match on a bare ','. There
// must be at least one non ',' character in the match. So the format
// specifier is placed directly next to the ':' so that the next
- // expected ' ' after the ':' will be the character it matches on
+ // expected ' ' after the ':' will be the character it matches on
// when there is no layer field.
int numPartsRead = sscanf( (char *) in_data, "Dialogue:%*128[^,],"
"%d:%d:%d.%d," // Start
@@ -232,10 +321,10 @@ static int parse_timing_from_ssa_packet( char *in_data, int64_t *in_start, int64
&end_hr, &end_min, &end_sec, &end_centi );
if ( numPartsRead != 8 )
return 1;
-
+
*in_start = SSA_2_HB_TIME(start_hr, start_min, start_sec, start_centi);
*in_stop = SSA_2_HB_TIME( end_hr, end_min, end_sec, end_centi);
-
+
return 0;
}
@@ -258,137 +347,7 @@ static uint8_t *find_field( uint8_t *pos, uint8_t *end, int fieldNum )
* SSA line format:
* Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
* 1 2 3 4 5 6 7 8 9 10
- */
-static hb_buffer_t *ssa_decode_line_to_utf8( uint8_t *in_data, int in_size, int in_sequence )
-{
- uint8_t *pos = in_data;
- uint8_t *end = in_data + in_size;
-
- // Parse values for in->s.start and in->s.stop
- int64_t in_start, in_stop;
- if ( parse_timing_from_ssa_packet( (char *) in_data, &in_start, &in_stop ) )
- goto fail;
-
- uint8_t *textFieldPos = find_field( pos, end, 10 );
- if ( textFieldPos == NULL )
- goto fail;
-
- // Count the number of style overrides in the Text field
- int numStyleOverrides = 0;
- pos = textFieldPos;
- while ( pos < end )
- {
- if (*pos++ == '{')
- {
- numStyleOverrides++;
- }
- }
-
- int maxOutputSize = (end - textFieldPos) + ((numStyleOverrides + 1) * MAX_OVERHEAD_PER_OVERRIDE);
- hb_buffer_t *out = hb_buffer_init( maxOutputSize );
- if ( out == NULL )
- return NULL;
-
- /*
- * The Text field contains plain text marked up with:
- * (1) '\n' -> space
- * (2) '\N' -> newline
- * (3) curly-brace control codes like '{\k44}' -> HTML tags / strip
- *
- * Perform the above conversions and copy it to the output packet
- */
- StyleSet prevStyles = 0;
- uint8_t *dst = out->data;
- pos = textFieldPos;
- while ( pos < end )
- {
- if ( pos[0] == '\\' && pos[1] == 'n' )
- {
- *dst++ = ' ';
- pos += 2;
- }
- else if ( pos[0] == '\\' && pos[1] == 'N' )
- {
- *dst++ = '\n';
- pos += 2;
- }
- else if ( pos[0] == '{' )
- {
- // Parse SSA style overrides and append appropriate HTML style tags
- StyleSet nextStyles = ssa_parse_style_override( pos, prevStyles );
- ssa_append_html_tags_for_style_change( &dst, prevStyles, nextStyles );
- prevStyles = nextStyles;
-
- // Skip past SSA control code
- while ( pos < end && *pos != '}' ) pos++;
- if ( pos < end && *pos == '}' ) pos++;
- }
- else
- {
- // Copy raw character
- *dst++ = *pos++;
- }
- }
-
- // Append closing HTML style tags
- ssa_append_html_tags_for_style_change( &dst, prevStyles, 0 );
-
- // Trim output buffer to the actual amount of data written
- out->size = dst - out->data;
-
- // Copy metadata from the input packet to the output packet
- out->s.frametype = HB_FRAME_SUBTITLE;
- out->s.start = in_start;
- out->s.stop = in_stop;
- out->sequence = in_sequence;
-
- return out;
-
-fail:
- hb_log( "decssasub: malformed SSA subtitle packet: %.*s\n", in_size, in_data );
- return NULL;
-}
-
-static hb_buffer_t * ssa_to_mkv_ssa( hb_work_object_t * w, hb_buffer_t * in )
-{
- hb_buffer_t * out_last = NULL;
- hb_buffer_t * out_first = NULL;
-
- // Store NULL after the end of the buffer to make using string processing safe
- hb_buffer_realloc(in, ++in->size);
- in->data[in->size - 1] = '\0';
-
- const char *EOL = "\r\n";
- char *curLine, *curLine_parserData;
- for ( curLine = strtok_r( (char *) in->data, EOL, &curLine_parserData );
- curLine;
- curLine = strtok_r( NULL, EOL, &curLine_parserData ) )
- {
- hb_buffer_t * out;
-
- out = ssa_decode_line_to_mkv_ssa( w, (uint8_t *) curLine, strlen( curLine ), in->sequence );
- if( out )
- {
- if ( out_last == NULL )
- {
- out_last = out_first = out;
- }
- else
- {
- out_last->next = out;
- out_last = out;
- }
- }
- }
-
- return out_first;
-}
-
-/*
- * SSA line format:
- * Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
- * 1 2 3 4 5 6 7 8 9 10
- *
+ *
* MKV-SSA packet format:
* ReadOrder,Marked, Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
* 1 2 3 4 5 6 7 8 9
@@ -397,22 +356,11 @@ static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *i
{
hb_work_private_t * pv = w->private_data;
hb_buffer_t * out;
-
+
// Parse values for in->s.start and in->s.stop
int64_t in_start, in_stop;
if ( parse_timing_from_ssa_packet( (char *) in_data, &in_start, &in_stop ) )
goto fail;
-
- if (pv->raw)
- {
- out = hb_buffer_init(in_size + 3);
- snprintf((char*)out->data, in_size + 3, "%s\r\n", in_data);
- out->s.frametype = HB_FRAME_SUBTITLE;
- out->s.start = in_start;
- out->s.stop = in_stop;
- out->sequence = in_sequence;
- return out;
- }
// Convert the SSA packet to MKV-SSA format, which is what libass expects
char *mkvIn;
@@ -424,18 +372,18 @@ static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *i
// format specifier "%*128[^,]" will not match on a bare ','. There
// must be at least one non ',' character in the match. So the format
// specifier is placed directly next to the ':' so that the next
- // expected ' ' after the ':' will be the character it matches on
+ // expected ' ' after the ':' will be the character it matches on
// when there is no layer field.
numPartsRead = sscanf( (char *)in_data, "Dialogue:%128[^,],", layerField );
if ( numPartsRead != 1 )
goto fail;
-
+
styleToTextFields = (char *)find_field( in_data, in_data + in_size, 4 );
if ( styleToTextFields == NULL ) {
free( layerField );
goto fail;
}
-
+
// The sscanf conversion above will result in an extra space
// before the layerField. Strip the space.
char *stripLayerField = layerField;
@@ -449,9 +397,9 @@ static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *i
strcat( mkvIn, "," );
strcat( mkvIn, stripLayerField );
strcat( mkvIn, "," );
- strcat( mkvIn, (char *) styleToTextFields );
-
- out->size = strlen(mkvIn);
+ strcat( mkvIn, (char *)styleToTextFields );
+
+ out->size = strlen(mkvIn) + 1;
out->s.frametype = HB_FRAME_SUBTITLE;
out->s.start = in_start;
out->s.stop = in_stop;
@@ -461,11 +409,11 @@ static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *i
{
hb_buffer_close(&out);
}
-
+
free( layerField );
-
+
return out;
-
+
fail:
hb_log( "decssasub: malformed SSA subtitle packet: %.*s\n", in_size, in_data );
return NULL;
@@ -479,24 +427,18 @@ static int decssaInit( hb_work_object_t * w, hb_job_t * job )
w->private_data = pv;
pv->job = job;
- if (job->mux & HB_MUX_MASK_AV && w->subtitle->config.dest != RENDERSUB )
- {
- pv->raw = 1;
- }
-
return 0;
}
static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
- hb_work_private_t * pv = w->private_data;
hb_buffer_t * in = *buf_in;
-
+
#if SSA_VERBOSE_PACKETS
printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->s.start/90, in->s.stop/90, in->size, in->data);
#endif
-
+
if ( in->size <= 0 )
{
*buf_out = in;
@@ -504,15 +446,7 @@ static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
return HB_WORK_DONE;
}
- if (w->subtitle->config.dest == PASSTHRUSUB &&
- (pv->job->mux & HB_MUX_MASK_MKV))
- {
- *buf_out = ssa_to_mkv_ssa(w, in);
- }
- else
- {
- *buf_out = ssa_decode_packet(w, in);
- }
+ *buf_out = ssa_decode_packet(w, in);
return HB_WORK_OK;
}
diff --git a/libhb/decssasub.h b/libhb/decssasub.h
new file mode 100644
index 000000000..65bc39187
--- /dev/null
+++ b/libhb/decssasub.h
@@ -0,0 +1,35 @@
+/* decssasub.h
+ *
+ * Copyright (c) 2003-2014 HandBrake Team
+ * This file is part of the HandBrake source code
+ * Homepage: <http://handbrake.fr/>.
+ * It may be used under the terms of the GNU General Public License v2.
+ * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+
+#ifndef __DECSSASUB_H__
+#define __DECSSASUB_H__
+
+typedef struct
+{
+ uint32_t flags;
+
+ uint32_t fg_rgb; // forground color
+ uint32_t alt_rgb; // secondary color
+ uint32_t ol_rgb; // outline color
+ uint32_t bg_rgb; // background color
+
+ uint32_t fg_alpha; // forground alpha
+ uint32_t alt_alpha; // secondary alpha
+ uint32_t ol_alpha; // outline alpha
+ uint32_t bg_alpha; // background alpha
+} hb_subtitle_style_t;
+
+#define HB_STYLE_FLAG_ITALIC 0x0001
+#define HB_STYLE_FLAG_BOLD 0x0002
+#define HB_STYLE_FLAG_UNDERLINE 0x0004
+
+char * hb_ssa_to_text(char *in, int *consumed, hb_subtitle_style_t *style);
+void hb_ssa_style_init(hb_subtitle_style_t *style);
+
+#endif // __DECSSASUB_H__
diff --git a/libhb/dectx3gsub.c b/libhb/dectx3gsub.c
index 4ca165ca8..7a38b1d84 100644
--- a/libhb/dectx3gsub.c
+++ b/libhb/dectx3gsub.c
@@ -9,17 +9,23 @@
/*
* Converts TX3G subtitles to UTF-8 subtitles with limited HTML-style markup (<b>, <i>, <u>).
- *
+ *
* TX3G == MPEG 4, Part 17 (ISO/IEC 14496-17) == 3GPP Timed Text (26.245)
* A full reference to the format can be found here:
* http://www.3gpp.org/ftp/Specs/html-info/26245.htm
- *
+ *
* @author David Foster (davidfstr)
*/
#include <stdlib.h>
#include <stdio.h>
#include "hb.h"
+#include "colormap.h"
+
+struct hb_work_private_s
+{
+ int line; // SSA line number
+};
typedef enum {
BOLD = 0x1,
@@ -27,9 +33,8 @@ typedef enum {
UNDERLINE = 0x4
} FaceStyleFlag;
-#define NUM_FACE_STYLE_FLAGS 3
-#define MAX_OPEN_TAG_SIZE 3 // "<b>"
-#define MAX_CLOSE_TAG_SIZE 4 // "</b>"
+#define MAX_MARKUP_LEN 40
+#define SSA_PREAMBLE_LEN 24
typedef struct {
uint16_t startChar; // NOTE: indices in terms of *character* (not: byte) positions
@@ -48,8 +53,6 @@ typedef struct {
#define SKIP_ARRAY(n) pos += n;
#define WRITE_CHAR(c) {dst[0]=c; dst += 1;}
-#define WRITE_START_TAG(c) {dst[0]='<'; dst[1]=c; dst[2]='>'; dst += 3;}
-#define WRITE_END_TAG(c) {dst[0]='<'; dst[1]='/'; dst[2]=c; dst[3]='>'; dst += 4;}
#define FOURCC(str) ((((uint32_t) str[0]) << 24) | \
(((uint32_t) str[1]) << 16) | \
@@ -57,125 +60,160 @@ typedef struct {
(((uint32_t) str[3]) << 0))
#define IS_10xxxxxx(c) ((c & 0xC0) == 0x80)
-static hb_buffer_t *tx3g_decode_to_utf8( hb_buffer_t *in )
+static int write_ssa_markup(char *dst, StyleRecord *style)
+{
+ if (style == NULL)
+ {
+ sprintf(dst, "{\\r}");
+ return strlen(dst);
+ }
+ sprintf(dst, "{\\i%d\\b%d\\u%d\\1c&H%X&\\1a&H%02X&}",
+ !!(style->faceStyleFlags & ITALIC),
+ !!(style->faceStyleFlags & BOLD),
+ !!(style->faceStyleFlags & UNDERLINE),
+ HB_RGB_TO_BGR(style->textColorRGBA >> 8),
+ 255 - (style->textColorRGBA & 0xFF)); // SSA alpha is inverted 0==opaque
+
+ return strlen(dst);
+}
+
+static hb_buffer_t *tx3g_decode_to_ssa(hb_buffer_t *in, int line)
{
uint8_t *pos = in->data;
uint8_t *end = in->data + in->size;
-
+
uint16_t numStyleRecords = 0;
-
- uint8_t *startStyle;
- uint8_t *endStyle;
-
+ StyleRecord *styleRecords = NULL;
+
/*
* Parse the packet as a TX3G TextSample.
- *
+ *
* Look for a single StyleBox ('styl') and read all contained StyleRecords.
* Ignore all other box types.
- *
+ *
* NOTE: Buffer overflows on read are not checked.
*/
uint16_t textLength = READ_U16();
uint8_t *text = READ_ARRAY(textLength);
- startStyle = calloc( textLength, 1 );
- endStyle = calloc( textLength, 1 );
- while ( pos < end ) {
+ while ( pos < end )
+ {
/*
* Read TextSampleModifierBox
*/
uint32_t size = READ_U32();
- if ( size == 0 ) {
+ if ( size == 0 )
+ {
size = pos - end; // extends to end of packet
}
- if ( size == 1 ) {
+ if ( size == 1 )
+ {
hb_log( "dectx3gsub: TextSampleModifierBox has unsupported large size" );
break;
}
uint32_t type = READ_U32();
- if ( type == FOURCC("uuid") ) {
+ if (type == FOURCC("uuid"))
+ {
hb_log( "dectx3gsub: TextSampleModifierBox has unsupported extended type" );
break;
}
-
- if ( type == FOURCC("styl") ) {
+
+ if (type == FOURCC("styl"))
+ {
// Found a StyleBox. Parse the contained StyleRecords
-
- if ( numStyleRecords != 0 ) {
+
+ if ( numStyleRecords != 0 )
+ {
hb_log( "dectx3gsub: found additional StyleBoxes on subtitle; skipping" );
SKIP_ARRAY(size);
continue;
}
-
+
numStyleRecords = READ_U16();
-
+ if (numStyleRecords > 0)
+ styleRecords = calloc(numStyleRecords, sizeof(StyleRecord));
+
int i;
- for (i=0; i<numStyleRecords; i++) {
- StyleRecord curRecord;
- curRecord.startChar = READ_U16();
- curRecord.endChar = READ_U16();
- curRecord.fontID = READ_U16();
- curRecord.faceStyleFlags = READ_U8();
- curRecord.fontSize = READ_U8();
- curRecord.textColorRGBA = READ_U32();
-
- startStyle[curRecord.startChar] |= curRecord.faceStyleFlags;
- endStyle[curRecord.endChar] |= curRecord.faceStyleFlags;
+ for (i = 0; i < numStyleRecords; i++)
+ {
+ styleRecords[i].startChar = READ_U16();
+ styleRecords[i].endChar = READ_U16();
+ styleRecords[i].fontID = READ_U16();
+ styleRecords[i].faceStyleFlags = READ_U8();
+ styleRecords[i].fontSize = READ_U8();
+ styleRecords[i].textColorRGBA = READ_U32();
}
- } else {
+ }
+ else
+ {
// Found some other kind of TextSampleModifierBox. Skip it.
SKIP_ARRAY(size);
}
}
-
+
/*
* Copy text to output buffer, and add HTML markup for the style records
*/
- int maxOutputSize = textLength + (numStyleRecords * NUM_FACE_STYLE_FLAGS * (MAX_OPEN_TAG_SIZE + MAX_CLOSE_TAG_SIZE));
+ int maxOutputSize = textLength + SSA_PREAMBLE_LEN + (numStyleRecords * MAX_MARKUP_LEN);
hb_buffer_t *out = hb_buffer_init( maxOutputSize );
if ( out == NULL )
goto fail;
uint8_t *dst = out->data;
int charIndex = 0;
- for ( pos = text, end = text + textLength; pos < end; pos++ ) {
- if (IS_10xxxxxx(*pos)) {
+ int styleIndex = 0;
+
+ sprintf((char*)dst, "%d,,Default,,0,0,0,,", line);
+ dst += strlen((char*)dst);
+ for (pos = text, end = text + textLength; pos < end; pos++)
+ {
+ if (IS_10xxxxxx(*pos))
+ {
// Is a non-first byte of a multi-byte UTF-8 character
WRITE_CHAR(*pos);
continue; // ...without incrementing 'charIndex'
}
-
- uint8_t plusStyles = startStyle[charIndex];
- uint8_t minusStyles = endStyle[charIndex];
-
- if (minusStyles & UNDERLINE)
- WRITE_END_TAG('u');
- if (minusStyles & ITALIC)
- WRITE_END_TAG('i');
- if (minusStyles & BOLD)
- WRITE_END_TAG('b');
-
- if (plusStyles & BOLD)
- WRITE_START_TAG('b');
- if (plusStyles & ITALIC)
- WRITE_START_TAG('i');
- if (plusStyles & UNDERLINE)
- WRITE_START_TAG('u');
-
- WRITE_CHAR(*pos);
+
+ if (styleIndex < numStyleRecords)
+ {
+ if (styleRecords[styleIndex].endChar == charIndex)
+ {
+ if (styleIndex + 1 >= numStyleRecords ||
+ styleRecords[styleIndex+1].startChar > charIndex)
+ {
+ dst += write_ssa_markup((char*)dst, NULL);
+ }
+ styleIndex++;
+ }
+ if (styleRecords[styleIndex].startChar == charIndex)
+ {
+ dst += write_ssa_markup((char*)dst, &styleRecords[styleIndex]);
+ }
+ }
+
+ if (*pos == '\n')
+ {
+ WRITE_CHAR('\\');
+ WRITE_CHAR('N');
+ }
+ else
+ {
+ WRITE_CHAR(*pos);
+ }
charIndex++;
}
-
+ *dst = '\0';
+ dst++;
+
// Trim output buffer to the actual amount of data written
out->size = dst - out->data;
-
+
// Copy metadata from the input packet to the output packet
out->s.frametype = HB_FRAME_SUBTITLE;
out->s.start = in->s.start;
out->s.stop = in->s.stop;
-
+
fail:
- free( startStyle );
- free( endStyle );
-
+ free(styleRecords);
+
return out;
}
@@ -191,55 +229,48 @@ fail:
static int dectx3gInit( hb_work_object_t * w, hb_job_t * job )
{
+ hb_work_private_t * pv;
+ pv = calloc( 1, sizeof( hb_work_private_t ) );
+ if (pv == NULL)
+ return 1;
+ w->private_data = pv;
+
+ // TODO:
+ // parse w->subtitle->extradata txg3 sample description into
+ // SSA format and replace extradata.
+ // For now we just create a generic SSA Script Info.
+ int height = job->title->height - job->crop[0] - job->crop[1];
+ int width = job->title->width - job->crop[2] - job->crop[3];
+ hb_subtitle_add_ssa_header(w->subtitle, width, height);
+
return 0;
}
static int dectx3gWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
+ hb_work_private_t * pv = w->private_data;
hb_buffer_t * in = *buf_in;
- hb_buffer_t * out = NULL;
-
- // Warn if the subtitle's duration has not been passed through by the demuxer,
- // which will prevent the subtitle from displaying at all
+
if ( in->s.stop == 0 ) {
hb_log( "dectx3gsub: subtitle packet lacks duration" );
}
-
- if ( in->size > 0 ) {
- out = tx3g_decode_to_utf8(in);
- } else {
- out = hb_buffer_init( 0 );
- }
-
- if ( out != NULL ) {
- // We shouldn't be storing the extra NULL character,
- // but the MP4 muxer expects this, unfortunately.
- if (out->size > 0 && out->data[out->size - 1] != '\0')
- {
- hb_buffer_realloc(out, ++out->size);
- out->data[out->size - 1] = '\0';
- }
-
- // If the input packet was non-empty, do not pass through
- // an empty output packet (even if the subtitle was empty),
- // as this would be interpreted as an end-of-stream
- if ( in->size > 0 && out->size == 0 ) {
- hb_buffer_close(&out);
- }
+
+ if (in->size == 0)
+ {
+ *buf_out = in;
+ *buf_in = NULL;
+ return HB_WORK_DONE;
}
-
- // Dispose the input packet, as it is no longer needed
- hb_buffer_close(&in);
-
- *buf_in = NULL;
- *buf_out = out;
+
+ *buf_out = tx3g_decode_to_ssa(in, ++pv->line);
+
return HB_WORK_OK;
}
static void dectx3gClose( hb_work_object_t * w )
{
- // nothing
+ free(w->private_data);
}
hb_work_object_t hb_dectx3gsub =
diff --git a/libhb/decutf8sub.c b/libhb/decutf8sub.c
index fac5b31a8..b8ea9bf5f 100644
--- a/libhb/decutf8sub.c
+++ b/libhb/decutf8sub.c
@@ -19,15 +19,33 @@
#include <stdlib.h>
#include <stdio.h>
#include "hb.h"
+#include "decsrtsub.h"
+
+struct hb_work_private_s
+{
+ int line; // SSA line number
+};
static int decutf8Init(hb_work_object_t *w, hb_job_t *job)
{
+ hb_work_private_t * pv;
+ pv = calloc( 1, sizeof( hb_work_private_t ) );
+ if (pv == NULL)
+ return 1;
+ w->private_data = pv;
+
+ // Generate generic SSA Script Info.
+ int height = job->title->height - job->crop[0] - job->crop[1];
+ int width = job->title->width - job->crop[2] - job->crop[3];
+ hb_subtitle_add_ssa_header(w->subtitle, width, height);
+
return 0;
}
static int decutf8Work(hb_work_object_t * w,
hb_buffer_t **buf_in, hb_buffer_t **buf_out)
{
+ hb_work_private_t * pv = w->private_data;
// Pass the packets through without modification
hb_buffer_t *out = *buf_in;
@@ -40,22 +58,19 @@ static int decutf8Work(hb_work_object_t * w,
hb_log("decutf8sub: subtitle packet lacks duration");
}
- // We shouldn't be storing the extra NULL character,
- // but the MP4 muxer expects this, unfortunately.
- if (out->size > 0 && out->data[out->size - 1] != '\0')
- {
- hb_buffer_realloc(out, ++out->size);
- out->data[out->size - 1] = '\0';
- }
+ hb_srt_to_ssa(out, ++pv->line);
*buf_in = NULL;
*buf_out = out;
+
+ if (out->size == 0)
+ return HB_WORK_DONE;
return HB_WORK_OK;
}
static void decutf8Close(hb_work_object_t *w)
{
- // nothing
+ free(w->private_data);
}
hb_work_object_t hb_decutf8sub =
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index 478991363..de8680e17 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -672,6 +672,11 @@ static int avformatInit( hb_mux_object_t * m )
track->st->codec->codec_id = AV_CODEC_ID_HDMV_PGS_SUBTITLE;
} break;
+ case CC608SUB:
+ case CC708SUB:
+ case TX3GSUB:
+ case SRTSUB:
+ case UTF8SUB:
case SSASUB:
{
if (job->mux == HB_MUX_AV_MP4)
@@ -697,18 +702,6 @@ static int avformatInit( hb_mux_object_t * m )
}
} break;
- case CC608SUB:
- case CC708SUB:
- case UTF8SUB:
- case TX3GSUB:
- case SRTSUB:
- {
- if (job->mux == HB_MUX_AV_MP4)
- track->st->codec->codec_id = AV_CODEC_ID_MOV_TEXT;
- else
- track->st->codec->codec_id = AV_CODEC_ID_TEXT;
- } break;
-
default:
continue;
}
@@ -974,7 +967,7 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu
AVPacket pkt;
int64_t dts, pts, duration = AV_NOPTS_VALUE;
hb_job_t *job = m->job;
- uint8_t tx3g_out[2048];
+ uint8_t sub_out[2048];
if (m->delay == AV_NOPTS_VALUE)
{
@@ -1129,8 +1122,7 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu
}
track->duration = pts;
}
- if (track->st->codec->codec_id == AV_CODEC_ID_MOV_TEXT ||
- track->st->codec->codec_id == AV_CODEC_ID_TEXT)
+ if (track->st->codec->codec_id == AV_CODEC_ID_MOV_TEXT)
{
uint8_t styleatom[2048];;
uint16_t stylesize = 0;
@@ -1150,14 +1142,48 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu
buffersize = strlen((char*)buffer);
/* Write the subtitle sample */
- memcpy( tx3g_out + 2, buffer, buffersize );
- memcpy( tx3g_out + 2 + buffersize, styleatom, stylesize);
- tx3g_out[0] = ( buffersize >> 8 ) & 0xff;
- tx3g_out[1] = buffersize & 0xff;
- pkt.data = tx3g_out;
+ memcpy( sub_out + 2, buffer, buffersize );
+ memcpy( sub_out + 2 + buffersize, styleatom, stylesize);
+ sub_out[0] = ( buffersize >> 8 ) & 0xff;
+ sub_out[1] = buffersize & 0xff;
+ pkt.data = sub_out;
pkt.size = buffersize + stylesize + 2;
}
}
+ if (track->st->codec->codec_id == AV_CODEC_ID_SSA &&
+ job->mux == HB_MUX_AV_MKV)
+ {
+ // avformat requires the this additional information
+ // which it parses and then strips away
+ int start_hh, start_mm, stop_hh, stop_mm, layer;
+ float start_ss, stop_ss;
+ char *ssa;
+
+ start_hh = buf->s.start / (90000 * 60 * 60);
+ start_mm = (buf->s.start / (90000 * 60));
+ start_ss = ((float)buf->s.start / 90000) - start_mm * 60;
+ start_mm %= 60;
+ stop_hh = buf->s.stop / (90000 * 60 * 60);
+ stop_mm = (buf->s.stop / (90000 * 60));
+ stop_ss = ((float)buf->s.stop / 90000) - stop_mm * 60;
+ stop_mm %= 60;
+
+ // Skip the read-order field
+ ssa = strchr((char*)buf->data, ',');
+ if (ssa != NULL)
+ ssa++;
+ // Skip the layer field
+ layer = strtol(ssa, NULL, 10);
+ ssa = strchr(ssa, ',');
+ if (ssa != NULL)
+ ssa++;
+ sprintf((char*)sub_out,
+ "Dialogue: %d,%d:%02d:%05.2f,%d:%02d:%05.2f,%s", layer,
+ start_hh, start_mm, start_ss,
+ stop_hh, stop_mm, stop_ss, ssa);
+ pkt.data = sub_out;
+ pkt.size = strlen((char*)sub_out) + 1;
+ }
pkt.convergence_duration = pkt.duration;
} break;
diff --git a/libhb/muxcommon.c b/libhb/muxcommon.c
index 4738bfa2a..68ba07d21 100644
--- a/libhb/muxcommon.c
+++ b/libhb/muxcommon.c
@@ -7,6 +7,7 @@
For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
*/
#include "hb.h"
+#include "decssasub.h"
#define MIN_BUFFERING (1024*1024*10)
#define MAX_BUFFERING (1024*1024*50)
@@ -47,9 +48,9 @@ typedef struct
uint32_t allEof; // valid bits in eof (all tracks)
uint32_t allRdy; // valid bits in rdy (audio & video tracks)
hb_track_t * track[32]; // array of tracks to mux ('ntrack' elements)
- // NOTE- this array could be dynamically
- // allocated but the eof & rdy logic has to
- // be changed to handle more than 32 tracks
+ // NOTE- this array could be dynamically
+ // allocated but the eof & rdy logic has to
+ // be changed to handle more than 32 tracks
// anyway so we keep it simple and fast.
int buffered_size;
} hb_mux_t;
@@ -256,9 +257,9 @@ static int muxWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
int more = mux->rdy;
// all tracks have at least 'interleave' ticks of data. Output
// all that we can in 'interleave' size chunks.
- while ( (( mux->rdy & mux->allRdy ) == mux->allRdy &&
+ while ( (( mux->rdy & mux->allRdy ) == mux->allRdy &&
more && mux->buffered_size > MIN_BUFFERING ) ||
- ( mux->eof == mux->allEof ) )
+ ( mux->eof == mux->allEof ) )
{
more = 0;
for ( i = 0; i < mux->ntracks; ++i )
@@ -380,7 +381,7 @@ void muxClose( hb_work_object_t * w )
}
}
}
-
+
for( i = 0; i < mux->ntracks; ++i )
{
hb_buffer_t * b;
@@ -555,245 +556,154 @@ hb_work_object_t hb_muxer =
muxClose
};
-typedef struct stylerecord_s {
- enum style_s {ITALIC, BOLD, UNDERLINE} style;
- uint16_t start;
- uint16_t stop;
- struct stylerecord_s *next;
-} stylerecord;
+#define TX3G_STYLES (HB_STYLE_FLAG_BOLD | \
+ HB_STYLE_FLAG_ITALIC | \
+ HB_STYLE_FLAG_UNDERLINE)
-static void hb_makestylerecord( stylerecord **stack,
- enum style_s style, int start )
+typedef struct style_context_s
{
- stylerecord *record = calloc( sizeof( stylerecord ), 1 );
+ uint8_t * style_atoms;
+ int style_atom_count;
+ hb_subtitle_style_t current_style;
+ int style_start;
+} style_context_t;
- if( record )
- {
- record->style = style;
- record->start = start;
- record->next = *stack;
- *stack = record;
- }
+static void update_style_atoms(style_context_t *ctx, int stop)
+{
+ uint8_t *style_entry;
+ uint8_t face = 0;
+
+ style_entry = ctx->style_atoms + 10 + (12 * ctx->style_atom_count);
+
+ if (ctx->current_style.flags & HB_STYLE_FLAG_BOLD)
+ face |= 1;
+ if (ctx->current_style.flags & HB_STYLE_FLAG_ITALIC)
+ face |= 2;
+ if (ctx->current_style.flags & HB_STYLE_FLAG_UNDERLINE)
+ face |= 4;
+
+ style_entry[0] = (ctx->style_start >> 8) & 0xff; // startChar
+ style_entry[1] = ctx->style_start & 0xff;
+ style_entry[2] = (stop >> 8) & 0xff; // endChar
+ style_entry[3] = stop & 0xff;
+ style_entry[4] = 0; // font-ID msb
+ style_entry[5] = 1; // font-ID lsb
+ style_entry[6] = face; // face-style-flags
+ style_entry[7] = 24; // font-size
+ style_entry[8] = (ctx->current_style.fg_rgb >> 16) & 0xff; // r
+ style_entry[9] = (ctx->current_style.fg_rgb >> 8) & 0xff; // g
+ style_entry[10] = (ctx->current_style.fg_rgb) & 0xff; // b
+ style_entry[11] = ctx->current_style.fg_alpha; // a
+
+ ctx->style_atom_count++;
}
-static void hb_makestyleatom( stylerecord *record, uint8_t *style)
+static void update_style(style_context_t *ctx,
+ hb_subtitle_style_t *style, int pos)
{
- uint8_t face = 1;
- hb_deep_log(3, "Made style '%s' from %d to %d",
- record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
-
- switch( record->style )
+ if (ctx->style_start < pos)
{
- case ITALIC:
- face = 2;
- break;
- case BOLD:
- face = 1;
- break;
- case UNDERLINE:
- face = 4;
- break;
- default:
- face = 2;
- break;
+ // do we need to add a style atom?
+ if (((ctx->current_style.flags ^ style->flags) & TX3G_STYLES) ||
+ ctx->current_style.fg_rgb != style->fg_rgb ||
+ ctx->current_style.fg_alpha != style->fg_alpha)
+ {
+ update_style_atoms(ctx, pos - 1);
+ }
}
+ ctx->current_style = *style;
+ ctx->style_start = pos;
+}
- style[0] = (record->start >> 8) & 0xff; // startChar
- style[1] = record->start & 0xff;
- style[2] = (record->stop >> 8) & 0xff; // endChar
- style[3] = record->stop & 0xff;
- style[4] = (1 >> 8) & 0xff; // font-ID
- style[5] = 1 & 0xff;
- style[6] = face; // face-style-flags: 1 bold; 2 italic; 4 underline
- style[7] = 24; // font-size
- style[8] = 255; // r
- style[9] = 255; // g
- style[10] = 255; // b
- style[11] = 255; // a
+static void style_context_init(style_context_t *ctx, uint8_t *style_atoms)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->style_atoms = style_atoms;
+ ctx->style_start = INT_MAX;
}
/*
* Copy the input to output removing markup and adding markup to the style
* atom where appropriate.
*/
-void hb_muxmp4_process_subtitle_style( uint8_t *input,
- uint8_t *output,
- uint8_t *style, uint16_t *stylesize )
+void hb_muxmp4_process_subtitle_style(uint8_t *input,
+ uint8_t *output,
+ uint8_t *style_atoms, uint16_t *stylesize)
{
- uint8_t *reader = input;
- uint8_t *writer = output;
- uint8_t stylecount = 0;
uint16_t utf8_count = 0; // utf8 count from start of subtitle
- stylerecord *stylestack = NULL;
- stylerecord *oldrecord = NULL;
+ int consumed, in_pos = 0, out_pos = 0, len, ii, lines;
+ style_context_t ctx;
+ hb_subtitle_style_t style;
+ char *text, *tmp;
+
+ *stylesize = 0;
+ style_context_init(&ctx, style_atoms);
- while(*reader != '\0') {
- if( ( *reader & 0xc0 ) == 0x80 )
+ hb_ssa_style_init(&style);
+
+ // Skip past the SSA preamble
+ text = (char*)input;
+ for (ii = 0; ii < 8; ii++)
+ {
+ tmp = strchr(text, ',');
+ if (tmp == NULL)
+ break;
+ text = tmp + 1;
+ }
+ in_pos = text - (char*)input;
+
+ while (input[in_pos] != '\0')
+ {
+ lines = 1;
+ text = hb_ssa_to_text((char*)input + in_pos, &consumed, &style);
+ if (text == NULL)
+ break;
+
+ // count UTF8 characters, and get length of text
+ len = 0;
+ for (ii = 0; text[ii] != '\0'; ii++)
{
- /*
- * Track the utf8_count when doing markup so that we get the tx3g stops
- * based on UTF8 chr counts rather than bytes.
- */
- utf8_count++;
- hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far",
- utf8_count);
- }
- if (*reader == '<') {
- /*
- * possible markup, peek at the next chr
- */
- switch(*(reader+1)) {
- case 'i':
- if (*(reader+2) == '>') {
- reader += 3;
- hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
- } else {
- *writer++ = *reader++;
- }
- break;
- case 'b':
- if (*(reader+2) == '>') {
- reader += 3;
- hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
- } else {
- *writer++ = *reader++;
- }
- break;
- case 'u':
- if (*(reader+2) == '>') {
- reader += 3;
- hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
- } else {
- *writer++ = *reader++;
- }
- break;
- case '/':
- switch(*(reader+2)) {
- case 'i':
- if (*(reader+3) == '>') {
- /*
- * Check whether we then immediately start more markup of the same type, if so then
- * lets not close it now and instead continue this markup.
- */
- if ((*(reader+4) && *(reader+4) == '<') &&
- (*(reader+5) && *(reader+5) == 'i') &&
- (*(reader+6) && *(reader+6) == '>')) {
- /*
- * Opening italics right after, so don't close off these italics.
- */
- hb_deep_log(3, "Joining two sets of italics");
- reader += (4 + 3);
- continue;
- }
-
-
- if ((*(reader+4) && *(reader+4) == ' ') &&
- (*(reader+5) && *(reader+5) == '<') &&
- (*(reader+6) && *(reader+6) == 'i') &&
- (*(reader+7) && *(reader+7) == '>')) {
- /*
- * Opening italics right after, so don't close off these italics.
- */
- hb_deep_log(3, "Joining two sets of italics (plus space)");
- reader += (4 + 4);
- *writer++ = ' ';
- continue;
- }
- if (stylestack && stylestack->style == ITALIC) {
- uint8_t style_record[12];
- stylestack->stop = writer - output - utf8_count;
- hb_makestyleatom(stylestack, style_record);
-
- memcpy(style + 10 + (12 * stylecount), style_record, 12);
- stylecount++;
-
- oldrecord = stylestack;
- stylestack = stylestack->next;
- free(oldrecord);
- } else {
- hb_error("Mismatched Subtitle markup '%s'", input);
- }
- reader += 4;
- } else {
- *writer++ = *reader++;
- }
- break;
- case 'b':
- if (*(reader+3) == '>') {
- if (stylestack && stylestack->style == BOLD) {
- uint8_t style_record[12];
- stylestack->stop = writer - output - utf8_count;
- hb_makestyleatom(stylestack, style_record);
-
- memcpy(style + 10 + (12 * stylecount), style_record, 12);
- stylecount++;
- oldrecord = stylestack;
- stylestack = stylestack->next;
- free(oldrecord);
- } else {
- hb_error("Mismatched Subtitle markup '%s'", input);
- }
-
- reader += 4;
- } else {
- *writer++ = *reader++;
- }
- break;
- case 'u':
- if (*(reader+3) == '>') {
- if (stylestack && stylestack->style == UNDERLINE) {
- uint8_t style_record[12];
- stylestack->stop = writer - output - utf8_count;
- hb_makestyleatom(stylestack, style_record);
-
- memcpy(style + 10 + (12 * stylecount), style_record, 12);
- stylecount++;
-
- oldrecord = stylestack;
- stylestack = stylestack->next;
- free(oldrecord);
- } else {
- hb_error("Mismatched Subtitle markup '%s'", input);
- }
- reader += 4;
- } else {
- *writer++ = *reader++;
- }
- break;
- default:
- *writer++ = *reader++;
- break;
- }
- break;
- default:
- *writer++ = *reader++;
- break;
+ if ((text[ii] & 0xc0) == 0x80)
+ {
+ utf8_count++;
+ hb_deep_log( 3, "mux: Counted %d UTF-8 chrs within subtitle",
+ utf8_count);
}
- } else if (*reader == '\r') {
- // skip '\r' and replace with '\n' if necessary
- if (*(++reader) != '\n') {
- *writer++ = '\n';
+ // By default tx3g only supports 2 lines of text
+ // To support more lines, we must enable the virtical placement
+ // flag in the tx3g atom and add tbox atoms to the sample
+ // data to set the vertical placement for each subtitle.
+ // Although tbox defines a rectangle, the QT spec says
+ // that only the vertical placement is honored (bummer).
+ if (text[ii] == '\n')
+ {
+ lines++;
+ if (lines > 2)
+ text[ii] = ' ';
}
- } else {
- *writer++ = *reader++;
+ len++;
}
+ strcpy((char*)output+out_pos, text);
+ free(text);
+ out_pos += len;
+ in_pos += consumed;
+ update_style(&ctx, &style, out_pos - utf8_count);
}
- *writer = '\0';
+ // null terminate output string
+ output[out_pos] = 0;
- if( stylecount )
+ if (ctx.style_atom_count > 0)
{
- *stylesize = 10 + ( stylecount * 12 );
+ *stylesize = 10 + (ctx.style_atom_count * 12);
- memcpy( style + 4, "styl", 4);
-
- style[0] = 0;
- style[1] = 0;
- style[2] = (*stylesize >> 8) & 0xff;
- style[3] = *stylesize & 0xff;
- style[8] = (stylecount >> 8) & 0xff;
- style[9] = stylecount & 0xff;
+ memcpy(style_atoms + 4, "styl", 4);
+ style_atoms[0] = 0;
+ style_atoms[1] = 0;
+ style_atoms[2] = (*stylesize >> 8) & 0xff;
+ style_atoms[3] = *stylesize & 0xff;
+ style_atoms[8] = (ctx.style_atom_count >> 8) & 0xff;
+ style_atoms[9] = ctx.style_atom_count & 0xff;
}
-
}
diff --git a/libhb/muxmkv.c b/libhb/muxmkv.c
index 0c2a28490..1250a681e 100644
--- a/libhb/muxmkv.c
+++ b/libhb/muxmkv.c
@@ -410,21 +410,17 @@ static int MKVInit( hb_mux_object_t * m )
track->codecPrivateSize = 0;
track->codecID = MK_SUBTITLE_PGS;
break;
+ case CC608SUB:
+ case CC708SUB:
+ case TX3GSUB:
+ case UTF8SUB:
+ case SRTSUB:
case SSASUB:
track->codecID = MK_SUBTITLE_SSA;
need_fonts = 1;
track->codecPrivate = subtitle->extradata;
track->codecPrivateSize = subtitle->extradata_size;
break;
- case CC608SUB:
- case CC708SUB:
- case UTF8SUB:
- case TX3GSUB:
- case SRTSUB:
- track->codecPrivate = NULL;
- track->codecPrivateSize = 0;
- track->codecID = MK_SUBTITLE_UTF8;
- break;
default:
continue;
}
diff --git a/libhb/rendersub.c b/libhb/rendersub.c
index eaec996c6..6d0772f22 100644
--- a/libhb/rendersub.c
+++ b/libhb/rendersub.c
@@ -9,7 +9,6 @@
#include "hb.h"
#include "hbffmpeg.h"
-#include "colormap.h"
#include <ass/ass.h>
struct hb_filter_private_s
@@ -25,15 +24,13 @@ struct hb_filter_private_s
ASS_Library * ssa;
ASS_Renderer * renderer;
ASS_Track * ssaTrack;
+ uint8_t script_initialized;
// SRT
int line;
hb_buffer_t * current_sub;
};
-static char* srt_markup_to_ssa(char *srt, int *len);
-static void srt_to_ssa(hb_buffer_t *srt_sub);
-
// VOBSUB
static int vobsub_init( hb_filter_object_t * filter, hb_filter_init_t * init );
@@ -512,10 +509,6 @@ static int ssa_init( hb_filter_object_t * filter,
return 1;
}
- // NOTE: The codec extradata is expected to be in MKV format
- ass_process_codec_private( pv->ssaTrack,
- (char *)filter->subtitle->extradata, filter->subtitle->extradata_size );
-
int width = init->width - ( pv->crop[2] + pv->crop[3] );
int height = init->height - ( pv->crop[0] + pv->crop[1] );
ass_set_frame_size( pv->renderer, width, height);
@@ -554,6 +547,19 @@ static int ssa_work( hb_filter_object_t * filter,
hb_buffer_t * in = *buf_in;
hb_buffer_t * sub;
+ if (!pv->script_initialized)
+ {
+ // NOTE: The codec extradata is expected to be in MKV format
+ // I would like to initialize this in ssa_init, but when we are
+ // transcoding text subtitles to SSA, the extradata does not
+ // get initialized until the decoder is initialized. Since
+ // decoder initialization happens after filter initialization,
+ // we need to postpone this.
+ ass_process_codec_private(pv->ssaTrack,
+ (char*)filter->subtitle->extradata,
+ filter->subtitle->extradata_size);
+ pv->script_initialized = 1;
+ }
if ( in->size <= 0 )
{
*buf_in = NULL;
@@ -584,28 +590,15 @@ static int ssa_work( hb_filter_object_t * filter,
static int textsub_init( hb_filter_object_t * filter,
hb_filter_init_t * init )
{
- const char * ssa_header =
- "[Script Info]\r\n"
- "ScriptType: v4.00+\r\n"
- "Collisions: Normal\r\n"
- "PlayResX: 1920\r\n"
- "PlayResY: 1080\r\n"
- "Timer: 100.0\r\n"
- "WrapStyle: 0\r\n"
- "\r\n"
- "[V4+ Styles]\r\n"
- "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\r\n"
- "Style: Default,Arial,72,&H00FFFFFF,&H00FFFFFF,&H000F0F0F,&H000F0F0F,0,0,0,0,100,100,0,0.00,1,2,3,2,20,20,20,0\r\n";
-
- filter->subtitle->extradata = (uint8_t*)ssa_header;
- filter->subtitle->extradata_size = strlen(ssa_header);
-
- int result = ssa_init(filter, init);
-
- filter->subtitle->extradata = NULL;
- filter->subtitle->extradata_size = 0;
-
- return result;
+ hb_filter_private_t * pv = filter->private_data;
+
+ int width = init->width - pv->crop[2] - pv->crop[3];
+ int height = init->height - pv->crop[0] - pv->crop[1];
+
+ // Text subtitles for which we create a dummy ASS header need
+ // to have the header rewritten with the correct dimensions.
+ hb_subtitle_add_ssa_header(filter->subtitle, width, height);
+ return ssa_init(filter, init);
}
static void textsub_close( hb_filter_object_t * filter )
@@ -615,20 +608,24 @@ static void textsub_close( hb_filter_object_t * filter )
static void process_sub(hb_filter_private_t *pv, hb_buffer_t *sub)
{
- char *ssa;
- int len;
- int64_t start, duration;
+ int64_t start, dur;
+ char *ssa, *tmp;
+
+ // libass expects every chunk to have a unique sequence number
+ // since we are repeating subs in some cases, we need to replace
+ // the sequence number.
+ tmp = strchr((char*)sub->data, ',');
+ if (tmp == NULL)
+ return;
- // Create SSA entry
- ssa = hb_strdup_printf("%d,,Default,,0,0,0,,%s", ++pv->line, sub->data);
- len = strlen(ssa);
+ ssa = hb_strdup_printf("%d%s", ++pv->line, tmp);
// Parse MKV-SSA packet
// SSA subtitles always have an explicit stop time, so we
// do not need to do special processing for stop == AV_NOPTS_VALUE
start = sub->s.start / 90;
- duration = (sub->s.stop - sub->s.start) / 90;
- ass_process_chunk( pv->ssaTrack, ssa, len, start, duration);
+ dur = (sub->s.stop - sub->s.start) / 90;
+ ass_process_chunk(pv->ssaTrack, ssa, sub->size, start, dur);
free(ssa);
}
@@ -640,6 +637,14 @@ static int textsub_work(hb_filter_object_t * filter,
hb_buffer_t * in = *buf_in;
hb_buffer_t * sub;
+ if (!pv->script_initialized)
+ {
+ ass_process_codec_private(pv->ssaTrack,
+ (char*)filter->subtitle->extradata,
+ filter->subtitle->extradata_size);
+ pv->script_initialized = 1;
+ }
+
if (in->size <= 0)
{
*buf_in = NULL;
@@ -651,17 +656,6 @@ static int textsub_work(hb_filter_object_t * filter,
// subtitle list
while ((sub = hb_fifo_get(filter->subtitle->fifo_out)))
{
- switch (pv->type)
- {
- case CC608SUB:
- case SRTSUB:
- case UTF8SUB:
- case TX3GSUB:
- srt_to_ssa(sub);
- break;
- default:
- break;
- }
// Subtitle formats such as CC can have stop times
// that are not known until an "erase display" command
// is encountered in the stream. For these formats
@@ -961,114 +955,3 @@ static void hb_rendersub_close( hb_filter_object_t * filter )
}
}
-static char* srt_markup_to_ssa(char *srt, int *len)
-{
- char terminator;
- char color[40];
- uint32_t rgb;
-
- *len = 0;
- if (srt[0] != '<' && srt[0] != '{')
- return NULL;
-
- if (srt[0] == '<')
- terminator = '>';
- else
- terminator = '}';
-
- if (srt[1] == 'i' && srt[2] == terminator)
- {
- *len = 3;
- return hb_strdup_printf("{\\i1}");
- }
- else if (srt[1] == 'b' && srt[2] == terminator)
- {
- *len = 3;
- return hb_strdup_printf("{\\b1}");
- }
- else if (srt[1] == 'u' && srt[2] == terminator)
- {
- *len = 3;
- return hb_strdup_printf("{\\u1}");
- }
- else if (srt[1] == '/' && srt[2] == 'i' && srt[3] == terminator)
- {
- *len = 4;
- return hb_strdup_printf("{\\i0}");
- }
- else if (srt[1] == '/' && srt[2] == 'b' && srt[3] == terminator)
- {
- *len = 4;
- return hb_strdup_printf("{\\b0}");
- }
- else if (srt[1] == '/' && srt[2] == 'u' && srt[3] == terminator)
- {
- *len = 4;
- return hb_strdup_printf("{\\u0}");
- }
- else if (srt[0] == '<' && !strncmp(srt + 1, "font", 4))
- {
- int match;
- match = sscanf(srt + 1, "font color=\"%39[^\"]\">", color);
- if (match != 1)
- {
- return NULL;
- }
- while (srt[*len] != '>') (*len)++;
- (*len)++;
- if (color[0] == '#')
- rgb = strtol(color + 1, NULL, 16);
- else
- rgb = hb_rgb_lookup_by_name(color);
- return hb_strdup_printf("{\\1c&H%X&}", HB_RGB_TO_BGR(rgb));
- }
- else if (srt[0] == '<' && srt[1] == '/' && !strncmp(srt + 2, "font", 4) &&
- srt[6] == '>')
- {
- *len = 7;
- return hb_strdup_printf("{\\1c&HFFFFFF&}");
- }
-
- return NULL;
-}
-
-static void srt_to_ssa(hb_buffer_t *sub_in)
-{
- char * srt = (char*)sub_in->data;
- // SSA markup expands a little over SRT, so allocate a bit of extra
- // space. More will be realloc'd if needed.
- hb_buffer_t * sub = hb_buffer_init(sub_in->size + 20);
- char * ssa, *ssa_markup;
- int skip, len, pos, ii;
-
- // Exchange data between input sub and new ssa_sub
- // After this, sub_in contains ssa data
- hb_buffer_swap_copy(sub_in, sub);
- ssa = (char*)sub_in->data;
- pos = 0;
- ii = 0;
- while (srt[ii] != '\0')
- {
- if ((ssa_markup = srt_markup_to_ssa(srt + ii, &skip)) != NULL)
- {
- len = strlen(ssa_markup);
- hb_buffer_realloc(sub_in, pos + len + 1);
- // After realloc, sub_in->data may change
- ssa = (char*)sub_in->data;
- snprintf(ssa + pos, len + 1, "%s", ssa_markup);
- free(ssa_markup);
- pos += len;
- ii += skip;
- }
- else
- {
- hb_buffer_realloc(sub_in, pos + 2);
- // After realloc, sub_in->data may change
- ssa = (char*)sub_in->data;
- ssa[pos++] = srt[ii++];
- }
- }
- ssa[pos] = '\0';
- hb_buffer_close(&sub);
-}
-