summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
Diffstat (limited to 'libhb')
-rw-r--r--libhb/decssasub.c182
-rw-r--r--libhb/decssasub.h22
-rw-r--r--libhb/internal.h4
-rw-r--r--libhb/muxavformat.c14
-rw-r--r--libhb/muxcommon.c234
-rw-r--r--libhb/ssautil.c925
-rw-r--r--libhb/ssautil.h34
-rw-r--r--libhb/sync.c15
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;
}