diff options
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/decssasub.c | 182 | ||||
-rw-r--r-- | libhb/decssasub.h | 22 | ||||
-rw-r--r-- | libhb/internal.h | 4 | ||||
-rw-r--r-- | libhb/muxavformat.c | 14 | ||||
-rw-r--r-- | libhb/muxcommon.c | 234 | ||||
-rw-r--r-- | libhb/ssautil.c | 925 | ||||
-rw-r--r-- | libhb/ssautil.h | 34 | ||||
-rw-r--r-- | libhb/sync.c | 15 |
8 files changed, 987 insertions, 443 deletions
diff --git a/libhb/decssasub.c b/libhb/decssasub.c index 2e95ba789..2bc06c7b9 100644 --- a/libhb/decssasub.c +++ b/libhb/decssasub.c @@ -44,184 +44,6 @@ struct hb_work_private_s #define SSA_VERBOSE_PACKETS 0 -static int ssa_update_style(char *ssa, hb_subtitle_style_t *style) -{ - int pos, end, index; - - if (ssa[0] != '{') - return 0; - - pos = 1; - while (ssa[pos] != '}' && ssa[pos] != '\0') - { - index = -1; - - // Skip any malformed markup junk - while (strchr("\\}", ssa[pos]) == NULL) pos++; - 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])) - { - 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 (ssa[pos] == 'c' && ssa[pos+1] == '&' && ssa[pos+2] == 'H') - { - // Font color markup - char *endptr; - uint32_t bgr; - - bgr = strtol(ssa + pos + 3, &endptr, 16); - if (*endptr == '&') - { - 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; - } - } - } - 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 == '&') - { - // 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; - } - if (ssa[pos] == '}') - pos++; - return pos; -} - -char * hb_ssa_to_text(char *in, int *consumed, hb_subtitle_style_t *style) -{ - 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; - - style->fg_alpha = 0xFF; - style->alt_alpha = 0xFF; - style->ol_alpha = 0xFF; - style->bg_alpha = 0xFF; -} - static int extradataInit( hb_work_private_t * pv ) { int events = 0; @@ -350,7 +172,7 @@ static int parse_timing( char *line, int64_t *start, int64_t *stop ) // specifier is placed directly next to the ':' so that the next // expected ' ' after the ':' will be the character it matches on // when there is no layer field. - int numPartsRead = sscanf( (char *) line, "Dialogue:%*128[^,]," + int numPartsRead = sscanf(line, "Dialogue:%*128[^,]," "%d:%d:%d.%d," // Start "%d:%d:%d.%d,", // End &start_hr, &start_min, &start_sec, &start_centi, @@ -478,9 +300,11 @@ static hb_buffer_t * ssa_read( hb_work_private_t * pv ) out = decode_line_to_mkv_ssa(pv, line, len); if (out != NULL) { + free(line); return out; } } + free(line); if (len < 0) { // Error or EOF diff --git a/libhb/decssasub.h b/libhb/decssasub.h index 174f056f6..19c7ce633 100644 --- a/libhb/decssasub.h +++ b/libhb/decssasub.h @@ -10,26 +10,4 @@ #ifndef __DECSSASUB_H__ #define __DECSSASUB_H__ -typedef struct -{ - uint32_t flags; - - uint32_t fg_rgb; // foreground color - uint32_t alt_rgb; // secondary color - uint32_t ol_rgb; // outline color - uint32_t bg_rgb; // background color - - uint32_t fg_alpha; // foreground 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/internal.h b/libhb/internal.h index 59b0677b8..cd46c1265 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -494,10 +494,6 @@ DECLARE_MUX( mp4 ); DECLARE_MUX( mkv ); DECLARE_MUX( avformat ); -void hb_muxmp4_process_subtitle_style(int height, - uint8_t * input, uint8_t ** output, - uint8_t ** style, uint16_t * stylesize); - void hb_deinterlace(hb_buffer_t *dst, hb_buffer_t *src); void hb_avfilter_combine( hb_list_t * list ); char * hb_append_filter_string(char * graph_str, char * filter_str); diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c index 60eba066f..ff041b47c 100644 --- a/libhb/muxavformat.c +++ b/libhb/muxavformat.c @@ -13,6 +13,7 @@ #include "libavutil/intreadwrite.h" #include "hb.h" +#include "ssautil.h" #include "lang.h" struct hb_mux_data_s @@ -33,7 +34,8 @@ struct hb_mux_data_s int64_t prev_chapter_tc; int16_t current_chapter; - AVBSFContext * bitstream_context; + AVBSFContext * bitstream_context; + hb_tx3g_style_context_t * tx3g; }; struct hb_mux_object_s @@ -850,6 +852,8 @@ static int avformatInit( hb_mux_object_t * m ) if (job->mux == HB_MUX_AV_MP4) { track->st->codecpar->codec_id = AV_CODEC_ID_MOV_TEXT; + track->tx3g = hb_tx3g_style_init( + job->height, (char*)subtitle->extradata); } else { @@ -1344,8 +1348,8 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu * creating style atoms for them. */ hb_muxmp4_process_subtitle_style( - job->height, buf->data, &buffer, - &styleatom, &stylesize ); + track->tx3g, buf->data, &buffer, + &styleatom, &stylesize); if (buffer != NULL) { @@ -1453,6 +1457,10 @@ static int avformatEnd(hb_mux_object_t *m) { av_bsf_free(&m->tracks[ii]->bitstream_context); } + if (m->tracks[ii]->tx3g) + { + hb_tx3g_style_close(&m->tracks[ii]->tx3g); + } } if (job->chapter_markers) diff --git a/libhb/muxcommon.c b/libhb/muxcommon.c index a61fc0803..5034b32e3 100644 --- a/libhb/muxcommon.c +++ b/libhb/muxcommon.c @@ -707,237 +707,3 @@ hb_work_object_t hb_muxer = muxClose }; -#define TX3G_STYLES (HB_STYLE_FLAG_BOLD | \ - HB_STYLE_FLAG_ITALIC | \ - HB_STYLE_FLAG_UNDERLINE) - -struct output_buf_s -{ - int alloc; - int size; - uint8_t * buf; -}; - -typedef struct style_context_s -{ - struct output_buf_s style_atoms; - int style_atom_count; - hb_subtitle_style_t current_style; - int style_start; - int height; -} style_context_t; - -static int check_realloc_output(struct output_buf_s * output, int size) -{ - if (output->alloc < size) - { - uint8_t * tmp; - - output->alloc = size + 1024; - output->size = size; - tmp = realloc(output->buf, output->alloc); - if (tmp == NULL) - { - hb_error("realloc failed!"); - free(output->buf); - output->size = 0; - output->alloc = 0; - output->buf = NULL; - return 0; - } - output->buf = tmp; - } - return 1; -} - -static int update_style_atoms(style_context_t *ctx, int stop) -{ - uint8_t * style_entry; - uint8_t face = 0; - int font_size; - int pos = 10 + (12 * ctx->style_atom_count); - int size = 10 + (12 * (ctx->style_atom_count + 1)); - - if (!check_realloc_output(&ctx->style_atoms, size)) - { - return 0; - } - style_entry = ctx->style_atoms.buf + pos; - - 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 - font_size = 0.05 * ctx->height; - if (font_size < 12) - { - font_size = 12; - } - else if (font_size > 255) - { - font_size = 255; - } - style_entry[7] = font_size; // 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++; - - return 1; -} - -static int update_style(style_context_t *ctx, - hb_subtitle_style_t *style, int pos) -{ - if (ctx->style_start < pos) - { - // 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) - { - if (!update_style_atoms(ctx, pos - 1)) - { - return 0; - } - ctx->current_style = *style; - ctx->style_start = pos; - } - } - else - { - ctx->current_style = *style; - ctx->style_start = pos; - } - return 1; -} - -static void style_context_init(style_context_t *ctx) -{ - memset(ctx, 0, sizeof(*ctx)); - ctx->style_atoms.buf = NULL; - ctx->style_atoms.size = 0; - ctx->style_atoms.alloc = 0; - 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(int height, - uint8_t * input, - uint8_t ** out_buf, - uint8_t ** out_style_atoms, - uint16_t * stylesize) -{ - uint16_t utf8_count = 0; // utf8 count from start of subtitle - int consumed, in_pos = 0, out_pos = 0, len, ii; - style_context_t ctx; - hb_subtitle_style_t style; - struct output_buf_s output; - char * text, * tmp; - - output.buf = NULL; - output.alloc = 0; - output.size = 0; - *out_buf = NULL; - *out_style_atoms = NULL; - *stylesize = 0; - - style_context_init(&ctx); - ctx.height = height; - 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; - - // Always allocate enough for empty string - if (!check_realloc_output(&output, 1)) - { - goto fail; - } - while (input[in_pos] != '\0') - { - 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++) - { - if ((text[ii] & 0xc0) == 0x80) - { - utf8_count++; - hb_deep_log( 3, "mux: Counted %d UTF-8 chrs within subtitle", - utf8_count); - } - len++; - } - if (!check_realloc_output(&output, out_pos + len + 1)) - { - goto fail; - } - strcpy((char*)output.buf + out_pos, text); - free(text); - out_pos += len; - in_pos += consumed; - if (!update_style(&ctx, &style, out_pos - utf8_count)) - { - goto fail; - } - } - // Return to default style at end of line, flushes any pending - // style changes - hb_ssa_style_init(&style); - if (!update_style(&ctx, &style, out_pos - utf8_count)) - { - goto fail; - } - - // null terminate output string - output.buf[out_pos] = 0; - - if (ctx.style_atom_count > 0) - { - *stylesize = 10 + (ctx.style_atom_count * 12); - - memcpy(ctx.style_atoms.buf + 4, "styl", 4); - - ctx.style_atoms.buf[0] = 0; - ctx.style_atoms.buf[1] = 0; - ctx.style_atoms.buf[2] = (*stylesize >> 8) & 0xff; - ctx.style_atoms.buf[3] = *stylesize & 0xff; - ctx.style_atoms.buf[8] = (ctx.style_atom_count >> 8) & 0xff; - ctx.style_atoms.buf[9] = ctx.style_atom_count & 0xff; - *out_style_atoms = ctx.style_atoms.buf; - } - *out_buf = output.buf; - return; - -fail: - free(output.buf); - free(ctx.style_atoms.buf); -} - diff --git a/libhb/ssautil.c b/libhb/ssautil.c new file mode 100644 index 000000000..7bc067a19 --- /dev/null +++ b/libhb/ssautil.c @@ -0,0 +1,925 @@ +/* ssautil.c + + Copyright (c) 2003-2019 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 + */ +#include <stdio.h> +#include <ctype.h> +#include "hb.h" +#include "ssautil.h" + +struct hb_subtitle_style_s +{ + char * name; + char * font_name; + int font_size; + + uint32_t flags; + + uint32_t fg_rgb; // foreground color + uint32_t alt_rgb; // secondary color + uint32_t ol_rgb; // outline color + uint32_t bg_rgb; // background color + + uint32_t fg_alpha; // foreground alpha + uint32_t alt_alpha; // secondary alpha + uint32_t ol_alpha; // outline alpha + uint32_t bg_alpha; // background alpha +}; + +struct hb_subtitle_style_context_s +{ + hb_subtitle_style_t current; + int event_style_default; + hb_subtitle_style_t * styles; + int styles_count; + int styles_size; + int style_default; +}; + +struct hb_tx3g_output_buf_s +{ + int alloc; + int size; + uint8_t * buf; +}; + +struct hb_tx3g_style_context_s +{ + hb_tx3g_output_buf_t style_atoms; + int style_atom_count; + int style_start; + int height; + hb_subtitle_style_context_t * in_style; + hb_subtitle_style_t out_style; + uint8_t flush; +}; + +static void ssa_style_reset(hb_subtitle_style_context_t * ctx) +{ + if (ctx->styles != NULL && ctx->style_default < ctx->styles_count) + { + ctx->current = ctx->styles[ctx->event_style_default]; + } + else + { + ctx->current.font_name = HB_FONT_SANS; + ctx->current.font_size = 72; + ctx->current.flags = 0; + ctx->current.fg_rgb = 0x00FFFFFF; + ctx->current.alt_rgb = 0x00FFFFFF; + ctx->current.ol_rgb = 0x000F0F0F; + ctx->current.bg_rgb = 0x000F0F0F; + + ctx->current.fg_alpha = 0xFF; + ctx->current.alt_alpha = 0xFF; + ctx->current.ol_alpha = 0xFF; + ctx->current.bg_alpha = 0xFF; + } +} + +static int ssa_style_set(hb_subtitle_style_context_t * ctx, const char * style) +{ + int ii; + + if (ctx->styles != NULL && style != NULL && style[0] != 0) + { + for (ii = 0; ii < ctx->styles_count; ii++) + { + if (!strcasecmp(ctx->styles[ii].name, style)) + { + ctx->current = ctx->styles[ii]; + return ii; + } + } + } + ssa_style_reset(ctx); + return ctx->style_default; +} + +static int ssa_update_style(const char *ssa, hb_subtitle_style_context_t *ctx) +{ + int pos, end, index; + hb_subtitle_style_t * style = &ctx->current; + + if (ssa[0] != '{') + return 0; + + pos = 1; + while (ssa[pos] != '}' && ssa[pos] != '\0') + { + index = -1; + + // Skip any malformed markup junk + while (strchr("\\}", ssa[pos]) == NULL) pos++; + 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])) + { + 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 (ssa[pos] == 'r') + { + // Style reset + char * style = strndup(ssa + pos + 1, end - (pos + 1)); + ssa_style_set(ctx, style); + free(style); + } + if (ssa[pos] == 'c' && ssa[pos+1] == '&' && ssa[pos+2] == 'H') + { + // Font color markup + char *endptr; + uint32_t bgr; + + bgr = strtol(ssa + pos + 3, &endptr, 16); + if (*endptr == '&') + { + 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; + } + } + } + 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 == '&') + { + // 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; + } + if (ssa[pos] == '}') + pos++; + return pos; +} + +static char * ssa_to_text(const char *in, int *consumed, + hb_subtitle_style_context_t *ctx) +{ + 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, ctx))) + { + *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; +} + +static char * get_field(char ** pos) +{ + char * result = NULL; + if (pos == NULL || *pos == NULL || **pos == 0) + { + return NULL; + } + char * start = *pos; + while (isspace(*start)) start++; + char * end = strchr(start, ','); + if (end != NULL) + { + result = strndup(start, end - start); + *pos = end + 1; + } + else + { + result = strdup(start); + *pos = NULL; + } + return result; +} + +static char * sgetline(char * str) +{ + char * eol; + + if (str == NULL) + { + return NULL; + } + + // find end of line + eol = strchr(str, '\n'); + if (eol != NULL) + { + if (eol > str && *(eol - 1) == '\r') + { + eol--; + } + } + if (eol != NULL) + { + return strndup(str, eol - str); + } + else + { + return strdup(str); + } +} + +static char ** get_fields(char * line, int last) +{ + int count = 0, ii; + char * pos; + + if (line == NULL || *line == 0) + { + return NULL; + } + + // count number of fields + count = 1; + pos = line; + while ((pos = strchr(pos, ',')) != NULL) + { + count++; + pos++; + } + if (last > 0 && count > last) + { + count = last; + } + char ** result = calloc(count + 1, sizeof(char*)); + pos = line; + for (ii = 0; ii < count - 1; ii++) + { + result[ii] = get_field(&pos); + printf("field (%s)\n", result[ii]); + } + result[ii] = strdup(pos); + + return result; +} + +static int field_index(char ** fields, char * name) +{ + int ii; + + if (fields == NULL || name == NULL) + { + return -1; + } + for (ii = 0; fields[ii] != NULL; ii++) + { + if (!strcasecmp(name, fields[ii])) + { + return ii; + } + } + return -1; +} + +static const char * field_value(char ** style, int index) +{ + if (index >= 0 && index < hb_str_vlen(style)) + { + return style[index]; + } + return NULL; +} + +typedef struct ssa_style_indicies_s +{ + int style_name_index; + int font_name_index; + int font_size_index; + int fg_color_index; + int alt_color_index; + int ol_color_index; + int bg_color_index; + int bold_index; + int italic_index; + int underline_index; +} ssa_style_indicies_t; + +static void fill_field_indicies(char **fields, ssa_style_indicies_t * indices) +{ + indices->style_name_index = field_index(fields, "Name"); + indices->font_name_index = field_index(fields, "Fontname"); + indices->font_size_index = field_index(fields, "Fontsize"); + indices->fg_color_index = field_index(fields, "PrimaryColour"); + indices->alt_color_index = field_index(fields, "SecondaryColour"); + indices->ol_color_index = field_index(fields, "OutlineColour"); + indices->bg_color_index = field_index(fields, "BackColour"); + indices->bold_index = field_index(fields, "Bold"); + indices->italic_index = field_index(fields, "Italic"); + indices->underline_index = field_index(fields, "Underline"); +} + +static int add_style(hb_subtitle_style_context_t *ctx, + char ** style, ssa_style_indicies_t *field_indices) +{ + const char * name; + const char * value; + int size; + uint32_t rgb; + uint32_t alpha; + uint32_t flag; + int style_index; + + if (style == NULL) + { + return 0; + } + if (ctx->styles_count + 1 > ctx->styles_size) + { + void * tmp; + ctx->styles_size = (ctx->styles_count + 1) * 2; + + tmp = realloc(ctx->styles, ctx->styles_size * + sizeof(hb_subtitle_style_t)); + if (tmp == NULL) + { + return 1; + } + ctx->styles = tmp; + } + style_index = ctx->styles_count; + + name = field_value(style, field_indices->style_name_index); + if (name == NULL) + { + name = "Default"; + } + if (!strcasecmp(name, "Default")) + { + ctx->style_default = style_index; + ctx->event_style_default = ctx->style_default; + } + ctx->styles[style_index].name = strdup(name); + + value = field_value(style, field_indices->font_name_index); + if (value == NULL) + { + value = HB_FONT_SANS; + } + ctx->styles[style_index].font_name = strdup(value); + + value = field_value(style, field_indices->font_size_index); + if (value == NULL) + { + size = 72; + } + else + { + size = strtol(value, NULL, 0); + } + ctx->styles[style_index].font_size = size; + + value = field_value(style, field_indices->fg_color_index); + if (value == NULL || strlen(value) < 3) + { + rgb = 0x00ffffff; + alpha = 0xff; + } + else + { + int abgr = strtol(value+2, NULL, 16); + rgb = HB_BGR_TO_RGB(abgr); + alpha = abgr >> 24; + } + ctx->styles[style_index].fg_rgb = rgb; + // SSA alpha is inverted 0 is opaque + ctx->styles[style_index].fg_alpha = 255 - alpha; + + value = field_value(style, field_indices->alt_color_index); + if (value == NULL || strlen(value) < 3) + { + rgb = 0x00ffffff; + alpha = 0xff; + } + else + { + int abgr = strtol(value+2, NULL, 16); + rgb = HB_BGR_TO_RGB(abgr); + alpha = abgr >> 24; + } + ctx->styles[style_index].alt_rgb = rgb; + ctx->styles[style_index].alt_alpha = alpha; + + value = field_value(style, field_indices->ol_color_index); + if (value == NULL || strlen(value) < 3) + { + rgb = 0x000f0f0f; + alpha = 0xff; + } + else + { + int abgr = strtol(value+2, NULL, 16); + rgb = HB_BGR_TO_RGB(abgr); + alpha = abgr >> 24; + } + ctx->styles[style_index].ol_rgb = rgb; + ctx->styles[style_index].ol_alpha = alpha; + + value = field_value(style, field_indices->bg_color_index); + if (value == NULL || strlen(value) < 3) + { + rgb = 0x000f0f0f; + alpha = 0xff; + } + else + { + int abgr = strtol(value+2, NULL, 16); + rgb = HB_BGR_TO_RGB(abgr); + alpha = abgr >> 24; + } + ctx->styles[style_index].bg_rgb = rgb; + ctx->styles[style_index].bg_alpha = alpha; + + ctx->styles[style_index].flags = 0; + + value = field_value(style, field_indices->bold_index); + if (value == NULL) + { + flag = HB_STYLE_FLAG_BOLD; + } + else + { + flag = strtol(value, NULL, 0) ? HB_STYLE_FLAG_BOLD : 0; + } + ctx->styles[style_index].flags |= flag; + + value = field_value(style, field_indices->italic_index); + if (value == NULL) + { + flag = HB_STYLE_FLAG_ITALIC; + } + else + { + flag = strtol(value, NULL, 0) ? HB_STYLE_FLAG_ITALIC : 0; + } + ctx->styles[style_index].flags |= flag; + + value = field_value(style, field_indices->underline_index); + if (value == NULL) + { + flag = HB_STYLE_FLAG_UNDERLINE; + } + else + { + flag = strtol(value, NULL, 0) ? HB_STYLE_FLAG_UNDERLINE : 0; + } + ctx->styles[style_index].flags |= flag; + + ctx->styles_count = style_index + 1; + + return 0; +} + +hb_subtitle_style_context_t * hb_subtitle_style_init(const char * ssa_header) +{ + hb_subtitle_style_context_t * ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) + { + return NULL; + } + if (ssa_header != NULL) + { + // Find beginning of styles + char * pos = strstr(ssa_header, "[V4"); + + if (pos != NULL) + { + pos = strstr(pos, "\nFormat:"); + if (pos != NULL) + { + char ** fields; + int next = 7; + char * line = sgetline(pos + 8); + + fields = get_fields(line, 0); + free(line); + + if (fields != NULL) + { + ssa_style_indicies_t field_indices; + + fill_field_indicies(fields, &field_indices); + + pos = strstr(pos, "\nStyle:"); + while (pos != NULL) + { + char ** style; + + line = sgetline(pos + next); + style = get_fields(line, 0); + free(line); + + if (add_style(ctx, style, &field_indices)) + { + hb_str_vfree(style); + break; + } + pos = strchr(pos + next, '\n'); + next = 1; + + hb_str_vfree(style); + } + + hb_str_vfree(fields); + } + } + } + } + ssa_style_reset(ctx); + return ctx; +} + +void hb_subtitle_style_close(hb_subtitle_style_context_t ** pctx) +{ + if (pctx == NULL || *pctx == NULL) + { + return; + } + hb_subtitle_style_context_t * ctx = *pctx; + + if (ctx->styles != NULL) + { + int ii; + + for (ii = 0; ii < ctx->styles_count; ii++) + { + free(ctx->styles[ii].name); + free(ctx->styles[ii].font_name); + } + } + free(ctx->styles); + free(ctx); + *pctx = NULL; +} + +#define TX3G_STYLES (HB_STYLE_FLAG_BOLD | \ + HB_STYLE_FLAG_ITALIC | \ + HB_STYLE_FLAG_UNDERLINE) + +static int check_realloc_output(hb_tx3g_output_buf_t * output, int size) +{ + if (output->alloc < size) + { + uint8_t * tmp; + + output->alloc = size + 1024; + output->size = size; + tmp = realloc(output->buf, output->alloc); + if (tmp == NULL) + { + hb_error("realloc failed!"); + free(output->buf); + output->size = 0; + output->alloc = 0; + output->buf = NULL; + return 0; + } + output->buf = tmp; + } + return 1; +} + +static int tx3g_update_style_atoms(hb_tx3g_style_context_t *ctx, int stop) +{ + uint8_t * style_entry; + uint8_t face = 0; + int font_size; + int pos = 10 + (12 * ctx->style_atom_count); + int size = 10 + (12 * (ctx->style_atom_count + 1)); + + if (!check_realloc_output(&ctx->style_atoms, size)) + { + return 0; + } + style_entry = ctx->style_atoms.buf + pos; + + if (ctx->out_style.flags & HB_STYLE_FLAG_BOLD) + face |= 1; + if (ctx->out_style.flags & HB_STYLE_FLAG_ITALIC) + face |= 2; + if (ctx->out_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 + font_size = 0.05 * ctx->height; + if (font_size < 12) + { + font_size = 12; + } + else if (font_size > 255) + { + font_size = 255; + } + style_entry[7] = font_size; // font-size + style_entry[8] = (ctx->out_style.fg_rgb >> 16) & 0xff; // r + style_entry[9] = (ctx->out_style.fg_rgb >> 8) & 0xff; // g + style_entry[10] = (ctx->out_style.fg_rgb) & 0xff; // b + style_entry[11] = ctx->out_style.fg_alpha; // a + + ctx->style_atom_count++; + + return 1; +} + +static int tx3g_update_style(hb_tx3g_style_context_t *ctx, int utf8_end_pos) +{ + hb_subtitle_style_t * style = &ctx->in_style->current; + + // do we need to add a style atom? + if (((ctx->out_style.flags ^ style->flags) & TX3G_STYLES) || + ctx->out_style.fg_rgb != style->fg_rgb || + ctx->out_style.fg_alpha != style->fg_alpha || + ctx->flush) + { + if (ctx->style_start < utf8_end_pos) + { + if (!tx3g_update_style_atoms(ctx, utf8_end_pos - 1)) + { + return 0; + } + ctx->style_start = utf8_end_pos; + } + ctx->out_style = *style; + ctx->flush = 0; + } + + return 1; +} + +hb_tx3g_style_context_t * +hb_tx3g_style_init(int height, const char * ssa_header) +{ + hb_tx3g_style_context_t * ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) + { + return NULL; + } + ctx->in_style = hb_subtitle_style_init(ssa_header); + ctx->height = height; + ctx->style_atoms.buf = NULL; + ctx->style_atoms.size = 0; + ctx->style_atoms.alloc = 0; + ctx->style_atom_count = 0; + ctx->style_start = 0; + ctx->out_style = ctx->in_style->current; + ctx->flush = 1; + + return ctx; +} + +void hb_tx3g_style_reset(hb_tx3g_style_context_t * ctx) +{ + ctx->style_atoms.buf = NULL; + ctx->style_atoms.size = 0; + ctx->style_atoms.alloc = 0; + ctx->style_atom_count = 0; + ctx->style_start = 0; + ctx->out_style = ctx->in_style->current; + ctx->flush = 1; +} + +void hb_tx3g_style_close(hb_tx3g_style_context_t ** pctx) +{ + if (pctx == NULL || *pctx == NULL) + { + return; + } + hb_tx3g_style_context_t * ctx = *pctx; + + hb_subtitle_style_close(&ctx->in_style); + free(ctx); + *pctx = NULL; +} + +/* + * Copy the input to output removing markup and adding markup to the style + * atom where appropriate. + */ +void hb_muxmp4_process_subtitle_style( + hb_tx3g_style_context_t * ctx, + uint8_t * input, + uint8_t ** out_buf, + uint8_t ** out_style_atoms, + uint16_t * stylesize) +{ + uint16_t utf8_pos = 0; + int consumed, in_pos = 0, out_pos = 0, len; + hb_tx3g_output_buf_t output; + char * text; + const char * ssa_text, * style; + + output.buf = NULL; + output.alloc = 0; + output.size = 0; + *out_buf = NULL; + *out_style_atoms = NULL; + *stylesize = 0; + + ssa_style_reset(ctx->in_style); + + // Skip past the SSA preamble + char ** event = get_fields((char*)input, 9); + if (hb_str_vlen(event) < 9) + { + // Not enough fields + goto fail; + } + + style = event[2]; + ssa_text = event[8]; + ctx->in_style->event_style_default = ssa_style_set(ctx->in_style, style); + hb_tx3g_style_reset(ctx); + + in_pos = 0; + // Always allocate enough for empty string + if (!check_realloc_output(&output, 1)) + { + goto fail; + } + while (ssa_text[in_pos] != '\0') + { + text = ssa_to_text(ssa_text + in_pos, &consumed, ctx->in_style); + if (text == NULL) + break; + + // count UTF8 characters, and get length of text + len = 0; + int ii, n; + for (ii = 0; text[ii] != '\0'; ii += n) + { + int jj; + char c = text[ii]; + + utf8_pos++; + if ((c & 0x80) == 0x00) n = 1; + else if ((c & 0xE0) == 0xC0) n = 2; + else if ((c & 0xF0) == 0xE0) n = 3; + else if ((c & 0xF8) == 0xF0) n = 4; + else n = 1; // invalid, but must handle + + // Prevent skipping null terminator + for (jj = 1; jj < n && text[ii + jj] != '\0'; jj++); + n = jj; + len += n; + } + if (!check_realloc_output(&output, out_pos + len + 1)) + { + goto fail; + } + strcpy((char*)output.buf + out_pos, text); + free(text); + out_pos += len; + in_pos += consumed; + if (!tx3g_update_style(ctx, utf8_pos)) + { + goto fail; + } + } + // Return to default style at end of line, flushes any pending + // style changes + ctx->flush = 1; + if (!tx3g_update_style(ctx, utf8_pos)) + { + goto fail; + } + + // null terminate output string + output.buf[out_pos] = 0; + + if (ctx->style_atom_count > 0) + { + *stylesize = 10 + (ctx->style_atom_count * 12); + + memcpy(ctx->style_atoms.buf + 4, "styl", 4); + + ctx->style_atoms.buf[0] = 0; + ctx->style_atoms.buf[1] = 0; + ctx->style_atoms.buf[2] = (*stylesize >> 8) & 0xff; + ctx->style_atoms.buf[3] = *stylesize & 0xff; + ctx->style_atoms.buf[8] = (ctx->style_atom_count >> 8) & 0xff; + ctx->style_atoms.buf[9] = ctx->style_atom_count & 0xff; + *out_style_atoms = ctx->style_atoms.buf; + } + *out_buf = output.buf; + hb_str_vfree(event); + + return; + +fail: + hb_str_vfree(event); + free(output.buf); + free(ctx->style_atoms.buf); +} diff --git a/libhb/ssautil.h b/libhb/ssautil.h new file mode 100644 index 000000000..319262b73 --- /dev/null +++ b/libhb/ssautil.h @@ -0,0 +1,34 @@ +/* ssautil.h + * + * Copyright (c) 2003-2019 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 __SSAUTIL_H__ +#define __SSAUTIL_H__ + +typedef struct hb_subtitle_style_s hb_subtitle_style_t; +typedef struct hb_subtitle_style_context_s hb_subtitle_style_context_t; + +typedef struct hb_tx3g_output_buf_s hb_tx3g_output_buf_t; +typedef struct hb_tx3g_style_context_s hb_tx3g_style_context_t; + +#define HB_STYLE_FLAG_ITALIC 0x0001 +#define HB_STYLE_FLAG_BOLD 0x0002 +#define HB_STYLE_FLAG_UNDERLINE 0x0004 + +hb_subtitle_style_context_t * hb_subtitle_style_init(const char * ssa_header); +hb_tx3g_style_context_t * hb_tx3g_style_init( + int height, const char * ssa_header); +void hb_subtitle_style_close(hb_subtitle_style_context_t ** ctx); +void hb_tx3g_style_close(hb_tx3g_style_context_t ** ctx); + +void hb_muxmp4_process_subtitle_style( + hb_tx3g_style_context_t * ctx, + uint8_t * input, uint8_t ** output, + uint8_t ** style, uint16_t * stylesize); + +#endif // __SSAUTIL_H__ diff --git a/libhb/sync.c b/libhb/sync.c index 7bd712022..17a0e73bd 100644 --- a/libhb/sync.c +++ b/libhb/sync.c @@ -2639,7 +2639,20 @@ static hb_buffer_t * merge_ssa(hb_buffer_t *a, hb_buffer_t *b) } if (text != NULL) { - len = sprintf((char*)buf->data, "%s\n%s", a->data, text); + // Strip trailing CR and/or LF + len = strlen((char*)a->data); + if (len > 0 && a->data[len - 1] == '\n') + { + a->data[len - 1] = 0; + len--; + if (len > 0 && a->data[len - 1] == '\r') + { + a->data[len - 1] = 0; + } + } + // Text subtitles are SSA internally. Use SSA newline code + // and force style reset at beginning of new line. + len = sprintf((char*)buf->data, "%s\\N{\\r}%s", a->data, text); if (len >= 0) buf->size = len + 1; } |