mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-01-06 23:31:13 +00:00
drawtext: manage memory allocation better
Call dtext_prepare_text as early as possible Do not draw if the memory allocation failed
This commit is contained in:
parent
a2fb4bcb01
commit
ec11ff8407
@ -369,9 +369,135 @@ static av_cold void uninit(AVFilterContext *ctx)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int is_newline(uint32_t c)
|
||||||
|
{
|
||||||
|
return (c == '\n' || c == '\r' || c == '\f' || c == '\v');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dtext_prepare_text(AVFilterContext *ctx, int width, int height)
|
||||||
|
{
|
||||||
|
DrawTextContext *dtext = ctx->priv;
|
||||||
|
uint32_t code = 0, prev_code = 0;
|
||||||
|
int x = 0, y = 0, i = 0, ret;
|
||||||
|
int text_height, baseline;
|
||||||
|
char *text = dtext->text;
|
||||||
|
uint8_t *p;
|
||||||
|
int str_w = 0, len;
|
||||||
|
int y_min = 32000, y_max = -32000;
|
||||||
|
FT_Vector delta;
|
||||||
|
Glyph *glyph = NULL, *prev_glyph = NULL;
|
||||||
|
Glyph dummy = { 0 };
|
||||||
|
|
||||||
|
#if HAVE_LOCALTIME_R
|
||||||
|
time_t now = time(0);
|
||||||
|
struct tm ltime;
|
||||||
|
uint8_t *buf = dtext->expanded_text;
|
||||||
|
int buf_size = dtext->expanded_text_size;
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
buf_size = 2*strlen(dtext->text)+1;
|
||||||
|
|
||||||
|
localtime_r(&now, <ime);
|
||||||
|
|
||||||
|
while ((buf = av_realloc(buf, buf_size))) {
|
||||||
|
*buf = 1;
|
||||||
|
if (strftime(buf, buf_size, dtext->text, <ime) != 0 || *buf == 0)
|
||||||
|
break;
|
||||||
|
buf_size *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
text = dtext->expanded_text = buf;
|
||||||
|
dtext->expanded_text_size = buf_size;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((len = strlen(text)) > dtext->nb_positions) {
|
||||||
|
FT_Vector *p = av_realloc(dtext->positions,
|
||||||
|
len * sizeof(*dtext->positions));
|
||||||
|
if (!p) {
|
||||||
|
av_freep(dtext->positions);
|
||||||
|
dtext->nb_positions = 0;
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
} else {
|
||||||
|
dtext->positions = p;
|
||||||
|
dtext->nb_positions = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load and cache glyphs */
|
||||||
|
for (i = 0, p = text; *p; i++) {
|
||||||
|
GET_UTF8(code, *p++, continue;);
|
||||||
|
|
||||||
|
/* get glyph */
|
||||||
|
dummy.code = code;
|
||||||
|
glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
|
||||||
|
if (!glyph)
|
||||||
|
ret = load_glyph(ctx, &glyph, code);
|
||||||
|
if (ret) return ret;
|
||||||
|
|
||||||
|
y_min = FFMIN(glyph->bbox.yMin, y_min);
|
||||||
|
y_max = FFMAX(glyph->bbox.yMax, y_max);
|
||||||
|
}
|
||||||
|
text_height = y_max - y_min;
|
||||||
|
baseline = y_max;
|
||||||
|
|
||||||
|
/* compute and save position for each glyph */
|
||||||
|
glyph = NULL;
|
||||||
|
for (i = 0, p = text; *p; i++) {
|
||||||
|
GET_UTF8(code, *p++, continue;);
|
||||||
|
|
||||||
|
/* skip the \n in the sequence \r\n */
|
||||||
|
if (prev_code == '\r' && code == '\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
prev_code = code;
|
||||||
|
if (is_newline(code)) {
|
||||||
|
str_w = FFMAX(str_w, x - dtext->x);
|
||||||
|
y += text_height;
|
||||||
|
x = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get glyph */
|
||||||
|
prev_glyph = glyph;
|
||||||
|
dummy.code = code;
|
||||||
|
glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
|
||||||
|
|
||||||
|
/* kerning */
|
||||||
|
if (dtext->use_kerning && prev_glyph && glyph->code) {
|
||||||
|
FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
|
||||||
|
ft_kerning_default, &delta);
|
||||||
|
x += delta.x >> 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x + glyph->bbox.xMax >= width) {
|
||||||
|
str_w = FFMAX(str_w, x);
|
||||||
|
y += text_height;
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save position */
|
||||||
|
dtext->positions[i].x = x + glyph->bitmap_left;
|
||||||
|
dtext->positions[i].y = y - glyph->bitmap_top + baseline;
|
||||||
|
if (code == '\t') x = (x / dtext->tabsize + 1)*dtext->tabsize;
|
||||||
|
else x += glyph->advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
str_w = FFMIN(width - 1, FFMAX(str_w, x));
|
||||||
|
y = FFMIN(y + text_height, height - 1);
|
||||||
|
|
||||||
|
dtext->w = str_w;
|
||||||
|
dtext->h = y;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int config_input(AVFilterLink *inlink)
|
static int config_input(AVFilterLink *inlink)
|
||||||
{
|
{
|
||||||
DrawTextContext *dtext = inlink->dst->priv;
|
AVFilterContext *ctx = inlink->dst;
|
||||||
|
DrawTextContext *dtext = ctx->priv;
|
||||||
const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
|
const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -398,7 +524,7 @@ static int config_input(AVFilterLink *inlink)
|
|||||||
dtext->shadowcolor[3] = rgba[3];
|
dtext->shadowcolor[3] = rgba[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return dtext_prepare_text(ctx, ctx->inputs[0]->w, ctx->inputs[0]->h);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GET_BITMAP_VAL(r, c) \
|
#define GET_BITMAP_VAL(r, c) \
|
||||||
@ -499,11 +625,6 @@ static inline void drawbox(AVFilterBufferRef *picref, unsigned int x, unsigned i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_newline(uint32_t c)
|
|
||||||
{
|
|
||||||
return (c == '\n' || c == '\r' || c == '\f' || c == '\v');
|
|
||||||
}
|
|
||||||
|
|
||||||
static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
|
static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
|
||||||
int width, int height, const uint8_t rgbcolor[4], const uint8_t yuvcolor[4], int x, int y)
|
int width, int height, const uint8_t rgbcolor[4], const uint8_t yuvcolor[4], int x, int y)
|
||||||
{
|
{
|
||||||
@ -542,128 +663,6 @@ static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dtext_prepare_text(AVFilterContext *ctx, int width, int height)
|
|
||||||
{
|
|
||||||
DrawTextContext *dtext = ctx->priv;
|
|
||||||
uint32_t code = 0, prev_code = 0;
|
|
||||||
int x = 0, y = 0, i = 0, ret;
|
|
||||||
int text_height, baseline;
|
|
||||||
char *text = dtext->text;
|
|
||||||
uint8_t *p;
|
|
||||||
int str_w = 0, len;
|
|
||||||
int y_min = 32000, y_max = -32000;
|
|
||||||
FT_Vector delta;
|
|
||||||
Glyph *glyph = NULL, *prev_glyph = NULL;
|
|
||||||
Glyph dummy = { 0 };
|
|
||||||
|
|
||||||
#if HAVE_LOCALTIME_R
|
|
||||||
time_t now = time(0);
|
|
||||||
struct tm ltime;
|
|
||||||
uint8_t *buf = dtext->expanded_text;
|
|
||||||
int buf_size = dtext->expanded_text_size;
|
|
||||||
|
|
||||||
if (!buf) {
|
|
||||||
buf_size = 2*strlen(dtext->text)+1;
|
|
||||||
buf = av_malloc(buf_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
localtime_r(&now, <ime);
|
|
||||||
|
|
||||||
do {
|
|
||||||
*buf = 1;
|
|
||||||
if (strftime(buf, buf_size, dtext->text, <ime) != 0 || *buf == 0)
|
|
||||||
break;
|
|
||||||
buf_size *= 2;
|
|
||||||
} while ((buf = av_realloc(buf, buf_size)));
|
|
||||||
|
|
||||||
if (!buf)
|
|
||||||
return AVERROR(ENOMEM);
|
|
||||||
text = dtext->expanded_text = buf;
|
|
||||||
dtext->expanded_text_size = buf_size;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((len = strlen(text)) > dtext->nb_positions) {
|
|
||||||
FT_Vector *p = av_realloc(dtext->positions,
|
|
||||||
len * sizeof(*dtext->positions));
|
|
||||||
if (!p) {
|
|
||||||
av_freep(dtext->positions);
|
|
||||||
dtext->nb_positions = 0;
|
|
||||||
return AVERROR(ENOMEM);
|
|
||||||
} else {
|
|
||||||
dtext->positions = p;
|
|
||||||
dtext->nb_positions = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* load and cache glyphs */
|
|
||||||
for (i = 0, p = text; *p; i++) {
|
|
||||||
GET_UTF8(code, *p++, continue;);
|
|
||||||
|
|
||||||
/* get glyph */
|
|
||||||
dummy.code = code;
|
|
||||||
glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
|
|
||||||
if (!glyph)
|
|
||||||
ret = load_glyph(ctx, &glyph, code);
|
|
||||||
if (ret) return ret;
|
|
||||||
|
|
||||||
y_min = FFMIN(glyph->bbox.yMin, y_min);
|
|
||||||
y_max = FFMAX(glyph->bbox.yMax, y_max);
|
|
||||||
}
|
|
||||||
text_height = y_max - y_min;
|
|
||||||
baseline = y_max;
|
|
||||||
|
|
||||||
/* compute and save position for each glyph */
|
|
||||||
glyph = NULL;
|
|
||||||
for (i = 0, p = text; *p; i++) {
|
|
||||||
GET_UTF8(code, *p++, continue;);
|
|
||||||
|
|
||||||
/* skip the \n in the sequence \r\n */
|
|
||||||
if (prev_code == '\r' && code == '\n')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
prev_code = code;
|
|
||||||
if (is_newline(code)) {
|
|
||||||
str_w = FFMAX(str_w, x - dtext->x);
|
|
||||||
y += text_height;
|
|
||||||
x = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get glyph */
|
|
||||||
prev_glyph = glyph;
|
|
||||||
dummy.code = code;
|
|
||||||
glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
|
|
||||||
|
|
||||||
/* kerning */
|
|
||||||
if (dtext->use_kerning && prev_glyph && glyph->code) {
|
|
||||||
FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
|
|
||||||
ft_kerning_default, &delta);
|
|
||||||
x += delta.x >> 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x + glyph->bbox.xMax >= width) {
|
|
||||||
str_w = FFMAX(str_w, x);
|
|
||||||
y += text_height;
|
|
||||||
x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* save position */
|
|
||||||
dtext->positions[i].x = x + glyph->bitmap_left;
|
|
||||||
dtext->positions[i].y = y - glyph->bitmap_top + baseline;
|
|
||||||
if (code == '\t') x = (x / dtext->tabsize + 1)*dtext->tabsize;
|
|
||||||
else x += glyph->advance;
|
|
||||||
}
|
|
||||||
|
|
||||||
str_w = FFMIN(width - 1, FFMAX(str_w, x));
|
|
||||||
y = FFMIN(y + text_height, height - 1);
|
|
||||||
|
|
||||||
dtext->w = str_w;
|
|
||||||
dtext->h = y;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
|
static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
|
||||||
int width, int height)
|
int width, int height)
|
||||||
{
|
{
|
||||||
@ -702,9 +701,10 @@ static void end_frame(AVFilterLink *inlink)
|
|||||||
{
|
{
|
||||||
AVFilterLink *outlink = inlink->dst->outputs[0];
|
AVFilterLink *outlink = inlink->dst->outputs[0];
|
||||||
AVFilterBufferRef *picref = inlink->cur_buf;
|
AVFilterBufferRef *picref = inlink->cur_buf;
|
||||||
|
int err = dtext_prepare_text(inlink->dst,
|
||||||
dtext_prepare_text(inlink->dst, picref->video->w, picref->video->h);
|
picref->video->w, picref->video->h);
|
||||||
draw_text(inlink->dst, picref, picref->video->w, picref->video->h);
|
if (!err)
|
||||||
|
draw_text(inlink->dst, picref, picref->video->w, picref->video->h);
|
||||||
|
|
||||||
avfilter_draw_slice(outlink, 0, picref->video->h, 1);
|
avfilter_draw_slice(outlink, 0, picref->video->h, 1);
|
||||||
avfilter_end_frame(outlink);
|
avfilter_end_frame(outlink);
|
||||||
|
Loading…
Reference in New Issue
Block a user