[FFmpeg-devel] [PATCH v16 15/16] avcodec/subtitles: Migrate subtitle encoders to frame-based API and provide a compatibility shim for the legacy api
Soft Works
softworkz at hotmail.com
Thu Nov 25 19:54:10 EET 2021
Also introduce deferred loading of ass headers for all cases where it can't be taken from the context of a decoder.
Signed-off-by: softworkz <softworkz at hotmail.com>
---
libavcodec/assenc.c | 81 ++++++++++++++++++++---------
libavcodec/avcodec.h | 7 +++
libavcodec/dvbsubenc.c | 85 +++++++++++++++---------------
libavcodec/dvdsubenc.c | 89 +++++++++++++++++---------------
libavcodec/encode.c | 98 ++++++++++++++++++++++++++++++++++-
libavcodec/movtextenc.c | 103 +++++++++++++++++++++++++------------
libavcodec/srtenc.c | 96 ++++++++++++++++++++++------------
libavcodec/tests/avcodec.c | 2 -
libavcodec/ttmlenc.c | 82 +++++++++++++++++++++++------
libavcodec/webvttenc.c | 73 +++++++++++++++++++-------
libavcodec/xsubenc.c | 65 ++++++++++++-----------
11 files changed, 541 insertions(+), 240 deletions(-)
diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index b0e475834b..2566b1d4dc 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -28,42 +28,77 @@
#include "libavutil/internal.h"
#include "libavutil/mem.h"
+static void check_write_header(AVCodecContext* avctx, const AVFrame* frame)
+{
+ if (avctx->extradata_size)
+ return;
+
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avctx->extradata_size = strlen(subtitle_header);
+ avctx->extradata = av_mallocz(frame->subtitle_header->size + 1);
+ memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+ avctx->extradata[avctx->extradata_size] = 0;
+ }
+
+ if (!avctx->extradata_size) {
+ const char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ avctx->extradata_size = strlen(subtitle_header);
+ avctx->extradata = av_mallocz(avctx->extradata_size + 1);
+ memcpy(avctx->extradata, subtitle_header, avctx->extradata_size);
+ avctx->extradata[avctx->extradata_size] = 0;
+ av_freep(&subtitle_header);
+ }
+}
+
static av_cold int ass_encode_init(AVCodecContext *avctx)
{
- avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
- if (!avctx->extradata)
- return AVERROR(ENOMEM);
- memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
- avctx->extradata_size = avctx->subtitle_header_size;
- avctx->extradata[avctx->extradata_size] = 0;
+ if (avctx->subtitle_header_size) {
+ avctx->extradata = av_malloc(avctx->subtitle_header_size + 1);
+ if (!avctx->extradata)
+ return AVERROR(ENOMEM);
+ memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size);
+ avctx->extradata_size = avctx->subtitle_header_size;
+ avctx->extradata[avctx->extradata_size] = 0;
+ }
+
return 0;
}
-static int ass_encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize,
- const AVSubtitle *sub)
+static int ass_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
- int i, len, total_len = 0;
+ int len, total_len = 0;
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ check_write_header(avctx, frame);
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
+
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
- len = av_strlcpy(buf+total_len, ass, bufsize-total_len);
+ if (ass) {
+ len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
- if (len > bufsize-total_len-1) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
- }
+ if (len > avpkt->size - 1) {
+ av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
+ return AVERROR_BUFFER_TOO_SMALL;
+ }
- total_len += len;
+ total_len += len;
+ }
}
- return total_len;
+ avpkt->size = total_len;
+ *got_packet = total_len > 0;
+
+ return 0;
}
#if CONFIG_SSA_ENCODER
@@ -73,7 +108,7 @@ const AVCodec ff_ssa_encoder = {
.type = AVMEDIA_TYPE_SUBTITLE,
.id = AV_CODEC_ID_ASS,
.init = ass_encode_init,
- .encode_sub = ass_encode_frame,
+ .encode2 = ass_encode_frame,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
#endif
@@ -85,7 +120,7 @@ const AVCodec ff_ass_encoder = {
.type = AVMEDIA_TYPE_SUBTITLE,
.id = AV_CODEC_ID_ASS,
.init = ass_encode_init,
- .encode_sub = ass_encode_frame,
+ .encode2 = ass_encode_frame,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
#endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 0c5819b116..304b35ba86 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -2949,10 +2949,17 @@ void av_parser_close(AVCodecParserContext *s);
* @{
*/
+ /**
+ * @deprecated Use @ref avcodec_encode_subtitle2() instead.
+ */
+attribute_deprecated
int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
const AVSubtitle *sub);
+int avcodec_encode_subtitle2(AVCodecContext* avctx, struct AVPacket* avpkt,
+ AVFrame* frame, int* got_packet);
+
/**
* @}
*/
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 322fc27cb4..4c6978b295 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -268,19 +268,19 @@ static int dvb_encode_rle8(uint8_t **pq, int buf_size,
return len;
}
-static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
- const AVSubtitle *h)
+static int dvbsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
DVBSubtitleContext *s = avctx->priv_data;
uint8_t *q, *pseg_len;
int page_id, region_id, clut_id, object_id, i, bpp_index, page_state;
+ size_t buf_size = avpkt->size;
-
- q = outbuf;
+ q = avpkt->data;
page_id = 1;
- if (h->num_rects && !h->rects)
+ if (frame->num_subtitle_areas && !frame->subtitle_areas)
return AVERROR(EINVAL);
if (avctx->width > 0 && avctx->height > 0) {
@@ -301,7 +301,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
/* page composition segment */
- if (buf_size < 8 + h->num_rects * 6)
+ if (buf_size < 8 + frame->num_subtitle_areas * 6)
return AVERROR_BUFFER_TOO_SMALL;
*q++ = 0x0f; /* sync_byte */
*q++ = 0x10; /* segment_type */
@@ -313,30 +313,30 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
/* page_version = 0 + page_state */
*q++ = (s->object_version << 4) | (page_state << 2) | 3;
- for (region_id = 0; region_id < h->num_rects; region_id++) {
+ for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
*q++ = region_id;
*q++ = 0xff; /* reserved */
- bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
- bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
+ bytestream_put_be16(&q, frame->subtitle_areas[region_id]->x); /* left pos */
+ bytestream_put_be16(&q, frame->subtitle_areas[region_id]->y); /* top pos */
}
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
- buf_size -= 8 + h->num_rects * 6;
+ buf_size -= 8 + frame->num_subtitle_areas * 6;
- if (h->num_rects) {
- for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
- if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
+ if (frame->num_subtitle_areas) {
+ for (clut_id = 0; clut_id < frame->num_subtitle_areas; clut_id++) {
+ if (buf_size < 6 + frame->subtitle_areas[clut_id]->nb_colors * 6)
return AVERROR_BUFFER_TOO_SMALL;
/* CLUT segment */
- if (h->rects[clut_id]->nb_colors <= 4) {
+ if (frame->subtitle_areas[clut_id]->nb_colors <= 4) {
/* 2 bpp, some decoders do not support it correctly */
bpp_index = 0;
- } else if (h->rects[clut_id]->nb_colors <= 16) {
+ } else if (frame->subtitle_areas[clut_id]->nb_colors <= 16) {
/* 4 bpp, standard encoding */
bpp_index = 1;
- } else if (h->rects[clut_id]->nb_colors <= 256) {
+ } else if (frame->subtitle_areas[clut_id]->nb_colors <= 256) {
/* 8 bpp, standard encoding */
bpp_index = 2;
} else {
@@ -353,12 +353,12 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
*q++ = clut_id;
*q++ = (0 << 4) | 0xf; /* version = 0 */
- for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
+ for(i = 0; i < frame->subtitle_areas[clut_id]->nb_colors; i++) {
*q++ = i; /* clut_entry_id */
*q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
{
int a, r, g, b;
- uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
+ uint32_t x= ((uint32_t*)frame->subtitle_areas[clut_id]->pal)[i];
a = (x >> 24) & 0xff;
r = (x >> 16) & 0xff;
g = (x >> 8) & 0xff;
@@ -372,22 +372,22 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
}
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
- buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
+ buf_size -= 6 + frame->subtitle_areas[clut_id]->nb_colors * 6;
}
- if (buf_size < h->num_rects * 22)
+ if (buf_size < frame->num_subtitle_areas * 22)
return AVERROR_BUFFER_TOO_SMALL;
- for (region_id = 0; region_id < h->num_rects; region_id++) {
+ for (region_id = 0; region_id < frame->num_subtitle_areas; region_id++) {
/* region composition segment */
- if (h->rects[region_id]->nb_colors <= 4) {
+ if (frame->subtitle_areas[region_id]->nb_colors <= 4) {
/* 2 bpp, some decoders do not support it correctly */
bpp_index = 0;
- } else if (h->rects[region_id]->nb_colors <= 16) {
+ } else if (frame->subtitle_areas[region_id]->nb_colors <= 16) {
/* 4 bpp, standard encoding */
bpp_index = 1;
- } else if (h->rects[region_id]->nb_colors <= 256) {
+ } else if (frame->subtitle_areas[region_id]->nb_colors <= 256) {
/* 8 bpp, standard encoding */
bpp_index = 2;
} else {
@@ -401,8 +401,8 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
q += 2; /* segment length */
*q++ = region_id;
*q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
- bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
- bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
+ bytestream_put_be16(&q, frame->subtitle_areas[region_id]->w); /* region width */
+ bytestream_put_be16(&q, frame->subtitle_areas[region_id]->h); /* region height */
*q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
*q++ = region_id; /* clut_id == region_id */
*q++ = 0; /* 8 bit fill colors */
@@ -416,9 +416,9 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
bytestream_put_be16(&pseg_len, q - pseg_len - 2);
}
- buf_size -= h->num_rects * 22;
+ buf_size -= frame->num_subtitle_areas * 22;
- for (object_id = 0; object_id < h->num_rects; object_id++) {
+ for (object_id = 0; object_id < frame->num_subtitle_areas; object_id++) {
int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
const uint8_t *bitmap, int linesize,
int w, int h);
@@ -427,13 +427,13 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
return AVERROR_BUFFER_TOO_SMALL;
/* bpp_index maths */
- if (h->rects[object_id]->nb_colors <= 4) {
+ if (frame->subtitle_areas[object_id]->nb_colors <= 4) {
/* 2 bpp, some decoders do not support it correctly */
dvb_encode_rle = dvb_encode_rle2;
- } else if (h->rects[object_id]->nb_colors <= 16) {
+ } else if (frame->subtitle_areas[object_id]->nb_colors <= 16) {
/* 4 bpp, standard encoding */
dvb_encode_rle = dvb_encode_rle4;
- } else if (h->rects[object_id]->nb_colors <= 256) {
+ } else if (frame->subtitle_areas[object_id]->nb_colors <= 256) {
/* 8 bpp, standard encoding */
dvb_encode_rle = dvb_encode_rle8;
} else {
@@ -463,19 +463,19 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
top_ptr = q;
ret = dvb_encode_rle(&q, buf_size,
- h->rects[object_id]->data[0],
- h->rects[object_id]->w * 2,
- h->rects[object_id]->w,
- h->rects[object_id]->h >> 1);
+ frame->subtitle_areas[object_id]->buf[0]->data,
+ frame->subtitle_areas[object_id]->w * 2,
+ frame->subtitle_areas[object_id]->w,
+ frame->subtitle_areas[object_id]->h >> 1);
if (ret < 0)
return ret;
buf_size -= ret;
bottom_ptr = q;
ret = dvb_encode_rle(&q, buf_size,
- h->rects[object_id]->data[0] + h->rects[object_id]->w,
- h->rects[object_id]->w * 2,
- h->rects[object_id]->w,
- h->rects[object_id]->h >> 1);
+ frame->subtitle_areas[object_id]->buf[0]->data + frame->subtitle_areas[object_id]->w,
+ frame->subtitle_areas[object_id]->w * 2,
+ frame->subtitle_areas[object_id]->w,
+ frame->subtitle_areas[object_id]->h >> 1);
if (ret < 0)
return ret;
buf_size -= ret;
@@ -502,7 +502,10 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
buf_size -= 6;
s->object_version = (s->object_version + 1) & 0xf;
- return q - outbuf;
+ avpkt->size = q - avpkt->data;
+ *got_packet = 1;
+
+ return 0;
}
const AVCodec ff_dvbsub_encoder = {
@@ -511,5 +514,5 @@ const AVCodec ff_dvbsub_encoder = {
.type = AVMEDIA_TYPE_SUBTITLE,
.id = AV_CODEC_ID_DVB_SUBTITLE,
.priv_data_size = sizeof(DVBSubtitleContext),
- .encode_sub = dvbsub_encode,
+ .encode2 = dvbsub_encode,
};
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index ff4fbed39d..74fefe5664 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -114,15 +114,14 @@ static int color_distance(uint32_t a, uint32_t b)
* Count colors used in a rectangle, quantizing alpha and grouping by
* nearest global palette entry.
*/
-static void count_colors(AVCodecContext *avctx, unsigned hits[33],
- const AVSubtitleRect *r)
+static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
+ const AVSubtitleArea *r)
{
DVDSubtitleContext *dvdc = avctx->priv_data;
unsigned count[256] = { 0 };
- uint32_t *palette = (uint32_t *)r->data[1];
uint32_t color;
int x, y, i, j, match, d, best_d, av_uninit(best_j);
- uint8_t *p = r->data[0];
+ uint8_t *p = r->buf[0]->data;
for (y = 0; y < r->h; y++) {
for (x = 0; x < r->w; x++)
@@ -132,7 +131,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
for (i = 0; i < 256; i++) {
if (!count[i]) /* avoid useless search */
continue;
- color = palette[i];
+ color = r->pal[i];
/* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
if (match) {
@@ -232,13 +231,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
}
}
-static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
+static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
{
int x, y;
uint8_t *p, *q;
- p = src->data[0];
- q = dst->data[0] + (src->x - dst->x) +
+ p = src->buf[0]->data;
+ q = dst->buf[0]->data + (src->x - dst->x) +
(src->y - dst->y) * dst->linesize[0];
for (y = 0; y < src->h; y++) {
for (x = 0; x < src->w; x++)
@@ -248,51 +247,56 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
}
}
-static int encode_dvd_subtitles(AVCodecContext *avctx,
- uint8_t *outbuf, int outbuf_size,
- const AVSubtitle *h)
+static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
DVDSubtitleContext *dvdc = avctx->priv_data;
uint8_t *q, *qq;
int offset1, offset2;
- int i, rects = h->num_rects, ret;
+ int ret = 0;
+ unsigned i, rects = frame->num_subtitle_areas;
unsigned global_palette_hits[33] = { 0 };
int cmap[256];
int out_palette[4];
int out_alpha[4];
- AVSubtitleRect vrect;
- uint8_t *vrect_data = NULL;
+ AVSubtitleArea vrect;
+ uint8_t* vrect_data = NULL, *outbuf = avpkt->data;
int x2, y2;
int forced = 0;
+ int outbuf_size = avpkt->size;
- if (rects == 0 || !h->rects)
+ if (rects == 0)
+ return 0;
+
+ if (!frame->subtitle_areas)
return AVERROR(EINVAL);
+
for (i = 0; i < rects; i++)
- if (h->rects[i]->type != SUBTITLE_BITMAP) {
+ if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) {
av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
return AVERROR(EINVAL);
}
/* Mark this subtitle forced if any of the rectangles is forced. */
for (i = 0; i < rects; i++)
- if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
+ if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
forced = 1;
break;
}
- vrect = *h->rects[0];
+ vrect = *frame->subtitle_areas[0];
if (rects > 1) {
/* DVD subtitles can have only one rectangle: build a virtual
rectangle containing all actual rectangles.
The data of the rectangles will be copied later, when the palette
is decided, because the rectangles may have different palettes. */
- int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
- int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
+ int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
+ int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
for (i = 1; i < rects; i++) {
- xmin = FFMIN(xmin, h->rects[i]->x);
- ymin = FFMIN(ymin, h->rects[i]->y);
- xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
- ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
+ xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
+ ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
+ xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
+ ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
}
vrect.x = xmin;
vrect.y = ymin;
@@ -304,27 +308,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
/* Count pixels outside the virtual rectangle as transparent */
global_palette_hits[0] = vrect.w * vrect.h;
for (i = 0; i < rects; i++)
- global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
+ global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
}
for (i = 0; i < rects; i++)
- count_colors(avctx, global_palette_hits, h->rects[i]);
+ count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
select_palette(avctx, out_palette, out_alpha, global_palette_hits);
if (rects > 1) {
- if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
+
+ vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);
+ if (!vrect.buf[0])
return AVERROR(ENOMEM);
- vrect.data [0] = vrect_data;
+
vrect.linesize[0] = vrect.w;
for (i = 0; i < rects; i++) {
- build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
+ build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
out_palette, out_alpha);
- copy_rectangle(&vrect, h->rects[i], cmap);
+ copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
}
for (i = 0; i < 4; i++)
cmap[i] = i;
} else {
- build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
+ build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
out_palette, out_alpha);
}
@@ -344,10 +350,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
ret = AVERROR_BUFFER_TOO_SMALL;
goto fail;
}
- dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
+ dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
vrect.w, (vrect.h + 1) >> 1, cmap);
offset2 = q - outbuf;
- dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
+ dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
vrect.w, vrect.h >> 1, cmap);
if (dvdc->even_rows_fix && (vrect.h & 1)) {
@@ -362,7 +368,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
bytestream_put_be16(&qq, q - outbuf);
// send start display command
- bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
+ bytestream_put_be16(&q, (frame->subtitle_start_time * 90) >> 10);
bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
*q++ = 0x03; // palette - 4 nibbles
*q++ = (out_palette[3] << 4) | out_palette[2];
@@ -394,7 +400,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
*q++ = 0xff; // terminating command
// send stop display command last
- bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
+ bytestream_put_be16(&q, (frame->subtitle_end_time*90) >> 10);
bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
*q++ = 0x02; // set end
*q++ = 0xff; // terminating command
@@ -403,7 +409,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
bytestream_put_be16(&qq, q - outbuf);
av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
- ret = q - outbuf;
+ avpkt->size = q - outbuf;
+ ret = 0;
+ *got_packet = 1;
fail:
av_free(vrect_data);
@@ -467,14 +475,13 @@ static int dvdsub_init(AVCodecContext *avctx)
return 0;
}
-static int dvdsub_encode(AVCodecContext *avctx,
- unsigned char *buf, int buf_size,
- const AVSubtitle *sub)
+static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
+ const struct AVFrame* frame, int* got_packet)
{
//DVDSubtitleContext *s = avctx->priv_data;
int ret;
- ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
+ ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
return ret;
}
@@ -499,7 +506,7 @@ const AVCodec ff_dvdsub_encoder = {
.type = AVMEDIA_TYPE_SUBTITLE,
.id = AV_CODEC_ID_DVD_SUBTITLE,
.init = dvdsub_init,
- .encode_sub = dvdsub_encode,
+ .encode2 = dvdsub_encode,
.priv_class = &dvdsubenc_class,
.priv_data_size = sizeof(DVDSubtitleContext),
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index dd25cf999b..dfbec01a3d 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -140,16 +140,110 @@ fail:
return ret;
}
+/**
+ * \brief
+ * \param avctx
+ * \param buf q
+ * \param buf_size
+ * \param sub
+ * \return
+ */
int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
const AVSubtitle *sub)
{
- int ret;
+ int ret = 0, got_packet = 0;
+ AVFrame *frame = NULL;
+ AVPacket* avpkt = NULL;
+
if (sub->start_display_time) {
av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
return -1;
}
- ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub);
+ // If the encoder implements the old API (encode_sub), call it directly:
+ if (avctx->codec->encode_sub) {
+ ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub);
+
+ avctx->frame_number++;
+ return ret;
+ }
+
+ // Create a temporary frame for calling the regular api:
+ frame = av_frame_alloc();
+ if (!frame) {
+ ret = AVERROR(ENOMEM);
+ goto exit;
+ }
+
+ frame->format = sub->format;
+ frame->type = AVMEDIA_TYPE_SUBTITLE;
+ ret = av_frame_get_buffer2(frame, 0);
+ if (ret < 0)
+ goto exit;
+
+ // Create a temporary packet
+ avpkt = av_packet_alloc();
+ if (!avpkt) {
+ ret = AVERROR(ENOMEM);
+ goto exit;
+ }
+
+ avpkt->data = buf;
+ avpkt->size = buf_size;
+
+ // Copy legacy subtitle data to temp frame
+ av_frame_put_subtitle(frame, sub);
+
+ ret = avcodec_encode_subtitle2(avctx, avpkt, frame, &got_packet);
+
+ if (got_packet)
+ ret = avpkt->size;
+
+ avpkt->data = NULL;
+
+exit:
+
+ av_packet_free(&avpkt);
+ av_frame_free(&frame);
+ return ret;
+}
+
+int avcodec_encode_subtitle2(AVCodecContext *avctx, AVPacket* avpkt,
+ AVFrame* frame, int* got_packet)
+{
+ int ret;
+
+ *got_packet = 0;
+ if (frame->subtitle_start_time) {
+ av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (avctx->codec->encode2) {
+
+ // Encoder implements the new/regular API
+ ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet);
+ }
+ else {
+
+ // Encoder implements the legacy API for subtitle encoding (encode_sub)
+ // copy subtitle data to a temporary AVSubtitle (legacy) buffer
+ AVSubtitle out_sub = { 0 };
+ av_frame_get_subtitle(&out_sub, frame);
+
+ ret = avctx->codec->encode_sub(avctx, avpkt->data, avpkt->size, &out_sub);
+
+ if (ret >= 0) {
+ avpkt->size = ret;
+ ret = 0;
+ *got_packet = 1;
+ }
+ else
+ avpkt->size = 0;
+
+ avsubtitle_free(&out_sub);
+ }
+
avctx->frame_number++;
return ret;
}
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index d50b34210f..f06df22aa9 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -73,6 +73,7 @@ typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
ASSStyle *ass_dialog_style;
StyleBox *style_attributes;
unsigned count;
@@ -330,12 +331,12 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx)
av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
- s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
- if (!s->ass_ctx)
- return AVERROR_INVALIDDATA;
- ret = encode_sample_description(avctx);
- if (ret < 0)
- return ret;
+ s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
+ if (s->ass_ctx) {
+ ret = encode_sample_description(avctx);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
@@ -634,35 +635,77 @@ static const ASSCodesCallbacks mov_text_callbacks = {
.end = mov_text_end_cb,
};
-static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
- int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+ MovTextContext* s = avctx->priv_data;
+ int ret;
+
+ if (s->ass_ctx && !s->is_default_ass_context)
+ // We already have a (non-default context)
+ return;
+
+ if (!frame->num_subtitle_areas)
+ // Don't need ass context for processing empty subtitle frames
+ return;
+
+ // The frame has content, so we need to set up a context
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avpriv_ass_split_free(s->ass_ctx);
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 0;
+ }
+ else if (!s->ass_ctx) {
+ char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 1;
+ av_free(subtitle_header);
+ }
+
+ if (s->ass_ctx && !avctx->extradata_size) {
+ ret = encode_sample_description(avctx);
+ if (ret < 0)
+ av_log(avctx, AV_LOG_ERROR, "Error during encode_sample_description().\n");
+ }
+}
+
+static int mov_text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
MovTextContext *s = avctx->priv_data;
ASSDialog *dialog;
- int i, length;
+ int i, ret = 0;
size_t j;
+ uint8_t* buf = avpkt->data;
+
+ ensure_ass_context(avctx, frame);
s->byte_count = 0;
s->text_pos = 0;
s->count = 0;
s->box_flags = 0;
- for (i = 0; i < sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ for (i = 0; i < frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
- dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
- if (!dialog)
- return AVERROR(ENOMEM);
- mov_text_dialog(s, dialog);
- avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
- avpriv_ass_free_dialog(&dialog);
+ if (ass) {
+ dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+ if (!dialog)
+ return AVERROR(ENOMEM);
+ mov_text_dialog(s, dialog);
+ avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
+ avpriv_ass_free_dialog(&dialog);
- for (j = 0; j < box_count; j++) {
- box_types[j].encode(s);
+ for (j = 0; j < box_count; j++) {
+ box_types[j].encode(s);
+ }
}
}
@@ -670,27 +713,23 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
buf += 2;
if (!av_bprint_is_complete(&s->buffer)) {
- length = AVERROR(ENOMEM);
+ ret = AVERROR(ENOMEM);
goto exit;
}
- if (!s->buffer.len) {
- length = 0;
- goto exit;
- }
-
- if (s->buffer.len > bufsize - 3) {
+ if (s->buffer.len > avpkt->size - 3) {
av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- length = AVERROR_BUFFER_TOO_SMALL;
+ ret = AVERROR_BUFFER_TOO_SMALL;
goto exit;
}
memcpy(buf, s->buffer.str, s->buffer.len);
- length = s->buffer.len + 2;
+ avpkt->size = s->buffer.len + 2;
+ *got_packet = 1;
exit:
av_bprint_clear(&s->buffer);
- return length;
+ return ret;
}
#define OFFSET(x) offsetof(MovTextContext, x)
@@ -715,7 +754,7 @@ const AVCodec ff_movtext_encoder = {
.priv_data_size = sizeof(MovTextContext),
.priv_class = &mov_text_encoder_class,
.init = mov_text_encode_init,
- .encode_sub = mov_text_encode_frame,
+ .encode2 = mov_text_encode_frame,
.close = mov_text_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
};
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index a7c5fccefe..2b29d60617 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -33,6 +33,7 @@
typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
AVBPrint buffer;
char stack[SRT_STACK_SIZE];
int stack_ptr;
@@ -130,14 +131,13 @@ static void srt_style_apply(SRTContext *s, const char *style)
}
}
-
static av_cold int srt_encode_init(AVCodecContext *avctx)
{
SRTContext *s = avctx->priv_data;
s->avctx = avctx;
- s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+ s->ass_ctx = avpriv_ass_split((char *)avctx->subtitle_header);
av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
- return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+ return 0;
}
static void srt_text_cb(void *priv, const char *text, int len)
@@ -227,58 +227,90 @@ static const ASSCodesCallbacks text_callbacks = {
.new_line = srt_new_line_cb,
};
-static int encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize, const AVSubtitle *sub,
- const ASSCodesCallbacks *cb)
+static void ensure_ass_context(SRTContext* s, const AVFrame* frame)
+{
+ if (s->ass_ctx && !s->is_default_ass_context)
+ // We already have a (non-default context)
+ return;
+
+ if (!frame->num_subtitle_areas)
+ // Don't need ass context for processing empty subtitle frames
+ return;
+
+ // The frame has content, so we need to set up a context
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avpriv_ass_split_free(s->ass_ctx);
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 0;
+ }
+ else if (!s->ass_ctx) {
+ char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 1;
+ av_free(subtitle_header);
+ }
+}
+
+static int encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet, const ASSCodesCallbacks* cb)
{
SRTContext *s = avctx->priv_data;
ASSDialog *dialog;
int i;
+ ensure_ass_context(s, frame);
+
av_bprint_clear(&s->buffer);
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ for (i=0; i< frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
- dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
- if (!dialog)
- return AVERROR(ENOMEM);
- s->alignment_applied = 0;
- if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
- srt_style_apply(s, dialog->style);
- avpriv_ass_split_override_codes(cb, s, dialog->text);
- avpriv_ass_free_dialog(&dialog);
+ if (ass) {
+ dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+ if (!dialog)
+ return AVERROR(ENOMEM);
+ s->alignment_applied = 0;
+ if (avctx->codec_id == AV_CODEC_ID_SUBRIP)
+ srt_style_apply(s, dialog->style);
+ avpriv_ass_split_override_codes(cb, s, dialog->text);
+ avpriv_ass_free_dialog(&dialog);
+ }
}
if (!av_bprint_is_complete(&s->buffer))
return AVERROR(ENOMEM);
- if (!s->buffer.len)
- return 0;
- if (s->buffer.len > bufsize) {
+ if (s->buffer.len > avpkt->size) {
av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
return AVERROR_BUFFER_TOO_SMALL;
}
- memcpy(buf, s->buffer.str, s->buffer.len);
- return s->buffer.len;
+ memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+ avpkt->size = s->buffer.len;
+ *got_packet = 1;
+
+ return 0;
}
-static int srt_encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int srt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
- return encode_frame(avctx, buf, bufsize, sub, &srt_callbacks);
+ return encode_frame(avctx, avpkt, frame, got_packet, &srt_callbacks);
}
-static int text_encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static int text_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
- return encode_frame(avctx, buf, bufsize, sub, &text_callbacks);
+ return encode_frame(avctx, avpkt, frame, got_packet, &text_callbacks);
}
static int srt_encode_close(AVCodecContext *avctx)
@@ -298,7 +330,7 @@ const AVCodec ff_srt_encoder = {
.id = AV_CODEC_ID_SUBRIP,
.priv_data_size = sizeof(SRTContext),
.init = srt_encode_init,
- .encode_sub = srt_encode_frame,
+ .encode2 = srt_encode_frame,
.close = srt_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
@@ -312,7 +344,7 @@ const AVCodec ff_subrip_encoder = {
.id = AV_CODEC_ID_SUBRIP,
.priv_data_size = sizeof(SRTContext),
.init = srt_encode_init,
- .encode_sub = srt_encode_frame,
+ .encode2 = srt_encode_frame,
.close = srt_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
@@ -326,7 +358,7 @@ const AVCodec ff_text_encoder = {
.id = AV_CODEC_ID_TEXT,
.priv_data_size = sizeof(SRTContext),
.init = srt_encode_init,
- .encode_sub = text_encode_frame,
+ .encode2 = text_encode_frame,
.close = srt_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 5d0ff9432c..bd979b2184 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -107,8 +107,6 @@ int main(void){
continue;
}
if (is_encoder) {
- if (codec->type == AVMEDIA_TYPE_SUBTITLE ^ !!codec->encode_sub)
- ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
if (!!codec->encode_sub + !!codec->encode2 + !!codec->receive_packet != 1)
ERR("Encoder %s does not implement exactly one encode API.\n");
if (codec->update_thread_context || codec->update_thread_context_for_user || codec->bsfs)
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index 083f2dd67a..3a4fdf2484 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -33,11 +33,15 @@
#include "libavutil/bprint.h"
#include "libavutil/internal.h"
#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
#include "ttmlenc.h"
+
typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
+ int extradata_written;
AVBPrint buffer;
} TTMLContext;
@@ -76,24 +80,69 @@ static const ASSCodesCallbacks ttml_callbacks = {
.new_line = ttml_new_line_cb,
};
-static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
- int bufsize, const AVSubtitle *sub)
+static int ttml_write_header_content(AVCodecContext* avctx);
+
+static void ensure_ass_context(AVCodecContext* avctx, const AVFrame* frame)
+{
+ TTMLContext* s = avctx->priv_data;
+ int ret;
+
+ if (s->ass_ctx && !s->is_default_ass_context)
+ // We already have a (non-default context)
+ return;
+
+ if (!frame->num_subtitle_areas)
+ // Don't need ass context for processing empty subtitle frames
+ return;
+
+ // The frame has content, so we need to set up a context
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ avpriv_ass_split_free(s->ass_ctx);
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 0;
+ }
+ else if (!s->ass_ctx) {
+ char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 1;
+ av_free(subtitle_header);
+ }
+
+ if (s->ass_ctx && !s->extradata_written) {
+ s->extradata_written = 1;
+ if ((ret = ttml_write_header_content(avctx)) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error writing header content.\n");
+ }
+ }
+}
+
+static int ttml_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
TTMLContext *s = avctx->priv_data;
ASSDialog *dialog;
int i;
+ ensure_ass_context(avctx, frame);
+
av_bprint_clear(&s->buffer);
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ for (i=0; i< frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
int ret;
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
+ if (!ass)
+ continue;
+
dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
if (!dialog)
return AVERROR(ENOMEM);
@@ -130,17 +179,18 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
if (!av_bprint_is_complete(&s->buffer))
return AVERROR(ENOMEM);
- if (!s->buffer.len)
- return 0;
// force null-termination, so in case our destination buffer is
// too small, the return value is larger than bufsize minus null.
- if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
+ if (s->buffer.len && av_strlcpy(avpkt->data, s->buffer.str, avpkt->size) > avpkt->size - 1) {
av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
return AVERROR_BUFFER_TOO_SMALL;
}
- return s->buffer.len;
+ avpkt->size = s->buffer.len;
+ *got_packet = 1;
+
+ return 0;
}
static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +420,13 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx)
s->avctx = avctx;
av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+ s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
- if (!(s->ass_ctx = avpriv_ass_split(avctx->subtitle_header))) {
- return AVERROR_INVALIDDATA;
- }
+ if (s->ass_ctx) {
+ if (ret = ttml_write_header_content(avctx) < 0)
+ return ret;
- if ((ret = ttml_write_header_content(avctx)) < 0) {
- return ret;
+ s->extradata_written = 1;
}
return 0;
@@ -389,7 +439,7 @@ const AVCodec ff_ttml_encoder = {
.id = AV_CODEC_ID_TTML,
.priv_data_size = sizeof(TTMLContext),
.init = ttml_encode_init,
- .encode_sub = ttml_encode_frame,
+ .encode2 = ttml_encode_frame,
.close = ttml_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
};
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 761099b69a..fbeefdbedd 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -32,6 +32,7 @@
typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
AVBPrint buffer;
unsigned timestamp_end;
int count;
@@ -155,43 +156,75 @@ static const ASSCodesCallbacks webvtt_callbacks = {
.end = webvtt_end_cb,
};
-static int webvtt_encode_frame(AVCodecContext *avctx,
- unsigned char *buf, int bufsize, const AVSubtitle *sub)
+static void ensure_ass_context(WebVTTContext* s, const AVFrame* frame)
+{
+ if (s->ass_ctx && !s->is_default_ass_context)
+ // We already have a (non-default context)
+ return;
+
+ if (!frame->num_subtitle_areas)
+ // Don't need ass context for processing empty subtitle frames
+ return;
+
+ // The frame has content, so we need to set up a context
+ if (frame->subtitle_header && frame->subtitle_header->size > 0) {
+ avpriv_ass_split_free(s->ass_ctx);
+ const char* subtitle_header = (char*)frame->subtitle_header->data;
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 0;
+ }
+ else if (!s->ass_ctx) {
+ char* subtitle_header = avpriv_ass_get_subtitle_header_default(0);
+ if (!subtitle_header)
+ return;
+
+ s->ass_ctx = avpriv_ass_split(subtitle_header);
+ s->is_default_ass_context = 1;
+ av_free(subtitle_header);
+ }
+}
+
+static int webvtt_encode_frame(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
WebVTTContext *s = avctx->priv_data;
ASSDialog *dialog;
int i;
+ ensure_ass_context(s, frame);
+
av_bprint_clear(&s->buffer);
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ for (i=0; i< frame->num_subtitle_areas; i++) {
+ const char *ass = frame->subtitle_areas[i]->ass;
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_ASS) {
+ av_log(avctx, AV_LOG_ERROR, "Only AV_SUBTITLE_FMT_ASS type supported.\n");
return AVERROR(EINVAL);
}
- dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
- if (!dialog)
- return AVERROR(ENOMEM);
- webvtt_style_apply(s, dialog->style);
- avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
- avpriv_ass_free_dialog(&dialog);
+ if (ass) {
+ dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
+ if (!dialog)
+ return AVERROR(ENOMEM);
+ webvtt_style_apply(s, dialog->style);
+ avpriv_ass_split_override_codes(&webvtt_callbacks, s, dialog->text);
+ avpriv_ass_free_dialog(&dialog);
+ }
}
if (!av_bprint_is_complete(&s->buffer))
return AVERROR(ENOMEM);
- if (!s->buffer.len)
- return 0;
- if (s->buffer.len > bufsize) {
+ if (s->buffer.len > avpkt->size) {
av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
return AVERROR_BUFFER_TOO_SMALL;
}
- memcpy(buf, s->buffer.str, s->buffer.len);
+ memcpy(avpkt->data, s->buffer.str, s->buffer.len);
+ avpkt->size = s->buffer.len;
+ *got_packet = s->buffer.len > 0;
- return s->buffer.len;
+ return 0;
}
static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +239,9 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx)
{
WebVTTContext *s = avctx->priv_data;
s->avctx = avctx;
- s->ass_ctx = avpriv_ass_split(avctx->subtitle_header);
+ s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
- return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
+ return 0;
}
const AVCodec ff_webvtt_encoder = {
@@ -218,7 +251,7 @@ const AVCodec ff_webvtt_encoder = {
.id = AV_CODEC_ID_WEBVTT,
.priv_data_size = sizeof(WebVTTContext),
.init = webvtt_encode_init,
- .encode_sub = webvtt_encode_frame,
+ .encode2 = webvtt_encode_frame,
.close = webvtt_encode_close,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c
index 03d0dc2d86..ef7c211351 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -111,39 +111,40 @@ static int make_tc(uint64_t ms, int *tc)
return ms > 99;
}
-static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
- int bufsize, const AVSubtitle *h)
+static int xsub_encode(AVCodecContext* avctx, AVPacket* avpkt,
+ const AVFrame* frame, int* got_packet)
{
- uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
- uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
+ const uint64_t startTime = frame->subtitle_pts / 1000; // FIXME: need better solution...
+ const uint64_t endTime = startTime + frame->subtitle_end_time - frame->subtitle_start_time;
int start_tc[4], end_tc[4];
- uint8_t *hdr = buf + 27; // Point behind the timestamp
+ uint8_t *hdr = avpkt->data + 27; // Point behind the timestamp
uint8_t *rlelenptr;
uint16_t width, height;
int i;
PutBitContext pb;
+ uint8_t* buf = avpkt->data;
- if (bufsize < 27 + 7*2 + 4*3) {
+ if (avpkt->size < 27 + 7*2 + 4*3) {
av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
return AVERROR_BUFFER_TOO_SMALL;
}
// TODO: support multiple rects
- if (h->num_rects != 1)
- av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
+ if (frame->num_subtitle_areas != 1)
+ av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", frame->num_subtitle_areas);
// TODO: render text-based subtitles into bitmaps
- if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
+ if (!frame->subtitle_areas[0]->buf[0]->data || !frame->subtitle_areas[0]->pal) {
av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
return AVERROR(EINVAL);
}
// TODO: color reduction, similar to dvdsub encoder
- if (h->rects[0]->nb_colors > 4)
- av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
+ if (frame->subtitle_areas[0]->nb_colors > 4)
+ av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", frame->subtitle_areas[0]->nb_colors);
// TODO: Palette swapping if color zero is not transparent
- if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
+ if (((uint32_t *)frame->subtitle_areas[0]->pal)[0] & 0xff000000)
av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
@@ -151,7 +152,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
return AVERROR(EINVAL);
}
- snprintf(buf, 28,
+ snprintf((char *)avpkt->data, 28,
"[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
start_tc[3], start_tc[2], start_tc[1], start_tc[0],
end_tc[3], end_tc[2], end_tc[1], end_tc[0]);
@@ -160,45 +161,47 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
// 2 pixels required on either side of subtitle.
// Possibly due to limitations of hardware renderers.
// TODO: check if the bitmap is already padded
- width = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
- height = FFALIGN(h->rects[0]->h, 2);
+ width = FFALIGN(frame->subtitle_areas[0]->w, 2) + PADDING * 2;
+ height = FFALIGN(frame->subtitle_areas[0]->h, 2);
bytestream_put_le16(&hdr, width);
bytestream_put_le16(&hdr, height);
- bytestream_put_le16(&hdr, h->rects[0]->x);
- bytestream_put_le16(&hdr, h->rects[0]->y);
- bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
- bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
+ bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x);
+ bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y);
+ bytestream_put_le16(&hdr, frame->subtitle_areas[0]->x + width -1);
+ bytestream_put_le16(&hdr, frame->subtitle_areas[0]->y + height -1);
rlelenptr = hdr; // Will store length of first field here later.
hdr+=2;
// Palette
for (i=0; i<4; i++)
- bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
+ bytestream_put_be24(&hdr, ((uint32_t *)frame->subtitle_areas[0]->pal)[i]);
// Bitmap
// RLE buffer. Reserve 2 bytes for possible padding after the last row.
- init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
- if (xsub_encode_rle(&pb, h->rects[0]->data[0],
- h->rects[0]->linesize[0] * 2,
- h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
+ init_put_bits(&pb, hdr, avpkt->size - (hdr - buf) - 2);
+ if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data,
+ frame->subtitle_areas[0]->linesize[0] * 2,
+ frame->subtitle_areas[0]->w, (frame->subtitle_areas[0]->h + 1) >> 1))
return AVERROR_BUFFER_TOO_SMALL;
bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
- if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
- h->rects[0]->linesize[0] * 2,
- h->rects[0]->w, h->rects[0]->h >> 1))
+ if (xsub_encode_rle(&pb, frame->subtitle_areas[0]->buf[0]->data + frame->subtitle_areas[0]->linesize[0],
+ frame->subtitle_areas[0]->linesize[0] * 2,
+ frame->subtitle_areas[0]->w, frame->subtitle_areas[0]->h >> 1))
return AVERROR_BUFFER_TOO_SMALL;
// Enforce total height to be a multiple of 2
- if (h->rects[0]->h & 1) {
- put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
+ if (frame->subtitle_areas[0]->h & 1) {
+ put_xsub_rle(&pb, frame->subtitle_areas[0]->w, PADDING_COLOR);
}
flush_put_bits(&pb);
- return hdr - buf + put_bytes_output(&pb);
+ avpkt->size = hdr - buf + put_bytes_output(&pb);
+ *got_packet = 1;
+ return 0;
}
static av_cold int xsub_encoder_init(AVCodecContext *avctx)
@@ -217,6 +220,6 @@ const AVCodec ff_xsub_encoder = {
.type = AVMEDIA_TYPE_SUBTITLE,
.id = AV_CODEC_ID_XSUB,
.init = xsub_encoder_init,
- .encode_sub = xsub_encode,
+ .encode2 = xsub_encode,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
--
2.30.2.windows.1
More information about the ffmpeg-devel
mailing list