Skip to content

Commit

Permalink
mux: eliminate 2048 byte size limit of SSA subtitles
Browse files Browse the repository at this point in the history
  • Loading branch information
jstebbins committed May 8, 2016
1 parent 3602907 commit 1e119dc
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 63 deletions.
4 changes: 2 additions & 2 deletions libhb/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,8 @@ DECLARE_MUX( mkv );
DECLARE_MUX( avformat );

void hb_muxmp4_process_subtitle_style( uint8_t *input,
uint8_t *output,
uint8_t *style, uint16_t *stylesize );
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 );
Expand Down
72 changes: 45 additions & 27 deletions libhb/muxavformat.c
Original file line number Diff line number Diff line change
Expand Up @@ -1035,10 +1035,10 @@ static int add_chapter(hb_mux_object_t *m, int64_t start, int64_t end, char * ti

static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *buf)
{
AVPacket pkt;
int64_t dts, pts, duration = AV_NOPTS_VALUE;
hb_job_t *job = m->job;
uint8_t sub_out[2048];
AVPacket pkt;
int64_t dts, pts, duration = AV_NOPTS_VALUE;
hb_job_t * job = m->job;
uint8_t * sub_out = NULL;

if (track->type == MUX_TYPE_VIDEO && (job->mux & HB_MUX_MASK_MP4))
{
Expand Down Expand Up @@ -1197,30 +1197,37 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu
}
if (track->st->codec->codec_id == AV_CODEC_ID_MOV_TEXT)
{
uint8_t styleatom[2048];;
uint16_t stylesize = 0;
uint8_t buffer[2048];
uint16_t buffersize = 0;

*buffer = '\0';
uint8_t * styleatom;
uint16_t stylesize = 0;
uint8_t * buffer;
uint16_t buffersize = 0;

/*
* Copy the subtitle into buffer stripping markup and creating
* style atoms for them.
* Copy the subtitle into buffer stripping markup and
* creating style atoms for them.
*/
hb_muxmp4_process_subtitle_style( buf->data,
buffer,
styleatom, &stylesize );

buffersize = strlen((char*)buffer);

/* Write the subtitle sample */
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;
hb_muxmp4_process_subtitle_style(buf->data, &buffer,
&styleatom, &stylesize );

if (buffer != NULL)
{
buffersize = strlen((char*)buffer);
if (styleatom == NULL)
{
stylesize = 0;
}
sub_out = malloc(2 + buffersize + stylesize);

/* Write the subtitle sample */
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;
}
free(buffer);
free(styleatom);
}
}
if (track->st->codec->codec_id == AV_CODEC_ID_SSA &&
Expand Down Expand Up @@ -1250,16 +1257,23 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu
ssa = strchr(ssa, ',');
if (ssa != NULL)
ssa++;
sprintf((char*)sub_out,
sub_out = (uint8_t*)hb_strdup_printf(
"Dialogue: %d,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s",
layer,
start_hh, start_mm, start_ss, start_ms,
stop_hh, stop_mm, stop_ss, stop_ms, ssa);
pkt.data = sub_out;
pkt.size = strlen((char*)sub_out) + 1;
}
if (pkt.data == NULL)
{
// Memory allocation failure!
hb_error("avformatMux: subtitle memory allocation failure");
*job->done_error = HB_ERROR_UNKNOWN;
*job->die = 1;
return -1;
}
pkt.convergence_duration = pkt.duration;

} break;
case MUX_TYPE_AUDIO:
default:
Expand All @@ -1274,6 +1288,10 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu

pkt.stream_index = track->st->index;
int ret = av_interleaved_write_frame(m->oc, &pkt);
if (sub_out != NULL)
{
free(sub_out);
}
// Many avformat muxer functions do not check the error status
// of the AVIOContext. So we need to check it ourselves to detect
// write errors (like disk full condition).
Expand Down
145 changes: 111 additions & 34 deletions libhb/muxcommon.c
Original file line number Diff line number Diff line change
Expand Up @@ -706,20 +706,63 @@ hb_work_object_t hb_muxer =
HB_STYLE_FLAG_ITALIC | \
HB_STYLE_FLAG_UNDERLINE)

struct output_buf_s
{
int alloc;
int size;
uint8_t * buf;
};

typedef struct style_context_s
{
uint8_t * style_atoms;
struct output_buf_s style_atoms;
int style_atom_count;
hb_subtitle_style_t current_style;
int style_start;
} style_context_t;

static void update_style_atoms(style_context_t *ctx, int stop)
static int check_realloc_output(struct output_buf_s * output, int size)
{
uint8_t *style_entry;
uint8_t face = 0;
if (output->alloc < size)
{
uint8_t * tmp;

style_entry = ctx->style_atoms + 10 + (12 * ctx->style_atom_count);
if (output->alloc == 0)
{
output->alloc = 1024;
}
else
{
output->alloc *= 2;
}
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 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;
Expand All @@ -741,11 +784,15 @@ static void update_style_atoms(style_context_t *ctx, int stop)
style_entry[10] = (ctx->current_style.fg_rgb) & 0xff; // b
style_entry[11] = ctx->current_style.fg_alpha; // a

printf("rgb %x face %x alpha %d start %d stop %d\n", ctx->current_style.fg_rgb, face, ctx->current_style.fg_alpha, ctx->style_start, stop);

ctx->style_atom_count++;

return 1;
}

static void update_style(style_context_t *ctx,
hb_subtitle_style_t *style, int pos)
static int update_style(style_context_t *ctx,
hb_subtitle_style_t *style, int pos)
{
if (ctx->style_start < pos)
{
Expand All @@ -754,7 +801,10 @@ static void update_style(style_context_t *ctx,
ctx->current_style.fg_rgb != style->fg_rgb ||
ctx->current_style.fg_alpha != style->fg_alpha)
{
update_style_atoms(ctx, pos - 1);
if (!update_style_atoms(ctx, pos - 1))
{
return 0;
}
ctx->current_style = *style;
ctx->style_start = pos;
}
Expand All @@ -764,32 +814,42 @@ static void update_style(style_context_t *ctx,
ctx->current_style = *style;
ctx->style_start = pos;
}
return 1;
}

static void style_context_init(style_context_t *ctx, uint8_t *style_atoms)
static void style_context_init(style_context_t *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->style_atoms = style_atoms;
ctx->style_start = INT_MAX;
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(uint8_t *input,
uint8_t *output,
uint8_t *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;
char *text, *tmp;

*stylesize = 0;
style_context_init(&ctx, style_atoms);

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);
hb_ssa_style_init(&style);

// Skip past the SSA preamble
Expand Down Expand Up @@ -821,32 +881,49 @@ void hb_muxmp4_process_subtitle_style(uint8_t *input,
}
len++;
}
strcpy((char*)output+out_pos, text);
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;
update_style(&ctx, &style, out_pos - utf8_count);
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);
update_style(&ctx, &style, out_pos - utf8_count);
if (!update_style(&ctx, &style, out_pos - utf8_count))
{
goto fail;
}

// null terminate output string
output[out_pos] = 0;
output.buf[out_pos] = 0;

if (ctx.style_atom_count > 0)
{
*stylesize = 10 + (ctx.style_atom_count * 12);

memcpy(style_atoms + 4, "styl", 4);
memcpy(ctx.style_atoms.buf + 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;
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);
}

0 comments on commit 1e119dc

Please sign in to comment.