[FFmpeg-devel] [PATCH v2 20/26] avcodec/subtitles: Migrate subtitle encoders to frame-based API
ffmpegagent
ffmpegagent at gmail.com
Thu Jan 20 04:48:30 EET 2022
From: softworkz <softworkz at hotmail.com>
and provide a compatibility shim for the legacy api
Signed-off-by: softworkz <softworkz at hotmail.com>
---
libavcodec/assenc.c | 189 ++++++++++++++++++++++++++++++-------
libavcodec/avcodec.h | 5 +-
libavcodec/dvbsubenc.c | 96 ++++++++++---------
libavcodec/dvdsubenc.c | 102 ++++++++++++--------
libavcodec/encode.c | 61 +++++++++++-
libavcodec/movtextenc.c | 114 ++++++++++++++++------
libavcodec/srtenc.c | 108 ++++++++++++++-------
libavcodec/tests/avcodec.c | 2 -
libavcodec/ttmlenc.c | 101 +++++++++++++++-----
libavcodec/webvttenc.c | 86 ++++++++++++-----
libavcodec/xsubenc.c | 88 ++++++++++-------
11 files changed, 687 insertions(+), 265 deletions(-)
diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index b0e475834b..e1401b1ac5 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -22,70 +22,195 @@
#include <string.h>
#include "avcodec.h"
+#include "encode.h"
#include "libavutil/ass_internal.h"
#include "internal.h"
#include "libavutil/avstring.h"
#include "libavutil/internal.h"
#include "libavutil/mem.h"
+typedef struct {
+ AVCodecContext *avctx;
+ AVFrame* current_frame;
+ int have_frame;
+ int current_area;
+} AssEncContext;
+
+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;
+ AssEncContext *s = avctx->priv_data;
+
+ 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;
+ }
+
+ s->current_frame = av_frame_alloc();
+ return 0;
+}
+
+static av_cold int ass_encode_close(AVCodecContext *avctx)
+{
+ AssEncContext *s = avctx->priv_data;
+ av_frame_free(&s->current_frame);
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 ret;
+//// size_t req_len = 0, total_len = 0;
+////
+//// check_write_header(avctx, frame);
+////
+//// 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);
+//// }
+////
+//// if (ass)
+//// req_len += strlen(ass);
+//// }
+////
+//// ret = ff_get_encode_buffer(avctx, avpkt, req_len + 1, 0);
+//// if (ret < 0) {
+//// av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+//// return ret;
+//// }
+////
+//// for (unsigned i = 0; i < frame->num_subtitle_areas; i++) {
+//// const char *ass = frame->subtitle_areas[i]->ass;
+////
+//// if (ass) {
+//// size_t len = av_strlcpy((char *)avpkt->data + total_len, ass, avpkt->size - total_len);
+//// total_len += len;
+//// }
+//// }
+////
+//// avpkt->size = total_len;
+//// *got_packet = total_len > 0;
+////
+//// return 0;
+////}
+
+static int ass_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
{
- int i, len, total_len = 0;
+ AssEncContext *s = avctx->priv_data;
+ int ret;
+
+ if (!s->have_frame) {
+ s->current_area = 0;
+ ret = ff_encode_get_frame(avctx, s->current_frame);
+
+ if (ret < 0) {
+ av_frame_unref(s->current_frame);
+ return ret;
+ }
+
+ s->have_frame = 1;
+ }
- for (i=0; i<sub->num_rects; i++) {
- const char *ass = sub->rects[i]->ass;
+ check_write_header(avctx, s->current_frame);
- if (sub->rects[i]->type != SUBTITLE_ASS) {
- av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
+ if (s->current_frame->repeat_sub) {
+ av_frame_unref(s->current_frame);
+ s->have_frame = 0;
+ return AVERROR(EAGAIN);
+ }
+
+ if (s->current_area < s->current_frame->num_subtitle_areas) {
+ const AVSubtitleArea *area = s->current_frame->subtitle_areas[s->current_area];
+ const char *ass = area->ass;
+
+ if (area->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) {
+ size_t len = strlen(ass);
+
+ ret = ff_get_encode_buffer(avctx, avpkt, len + 1, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
+
+ len = av_strlcpy((char *)avpkt->data, ass, avpkt->size);
- if (len > bufsize-total_len-1) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ avpkt->size = len;
}
- total_len += len;
+ s->current_area++;
}
- return total_len;
+ if (s->current_area < s->current_frame->num_subtitle_areas)
+ return 0;
+
+ av_frame_unref(s->current_frame);
+ s->have_frame = 0;
+
+ return 0;
}
#if CONFIG_SSA_ENCODER
const AVCodec ff_ssa_encoder = {
- .name = "ssa",
- .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
- .type = AVMEDIA_TYPE_SUBTITLE,
- .id = AV_CODEC_ID_ASS,
- .init = ass_encode_init,
- .encode_sub = ass_encode_frame,
+ .name = "ssa",
+ .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+ .type = AVMEDIA_TYPE_SUBTITLE,
+ .id = AV_CODEC_ID_ASS,
+ .priv_data_size = sizeof(AssEncContext),
+ .init = ass_encode_init,
+ .close = ass_encode_close,
+ .receive_packet = ass_receive_packet,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
#endif
#if CONFIG_ASS_ENCODER
const AVCodec ff_ass_encoder = {
- .name = "ass",
- .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
- .type = AVMEDIA_TYPE_SUBTITLE,
- .id = AV_CODEC_ID_ASS,
- .init = ass_encode_init,
- .encode_sub = ass_encode_frame,
+ .name = "ass",
+ .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+ .type = AVMEDIA_TYPE_SUBTITLE,
+ .priv_data_size = sizeof(AssEncContext),
+ .id = AV_CODEC_ID_ASS,
+ .init = ass_encode_init,
+ .close = ass_encode_close,
+ .receive_packet = ass_receive_packet,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
#endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 9d59f6e840..93063dc6e9 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -2995,10 +2995,13 @@ 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);
-
/**
* @}
*/
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 322fc27cb4..3b5a76daa7 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -20,6 +20,7 @@
*/
#include "avcodec.h"
#include "bytestream.h"
+#include "encode.h"
#include "libavutil/colorspace.h"
typedef struct DVBSubtitleContext {
@@ -268,21 +269,29 @@ 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;
-
-
- q = outbuf;
+ size_t buf_size;
+ int ret;
page_id = 1;
- if (h->num_rects && !h->rects)
+ if (frame->num_subtitle_areas && !frame->subtitle_areas)
return AVERROR(EINVAL);
+ ret = ff_get_encode_buffer(avctx, avpkt, 1024 * 1024, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
+
+ buf_size = avpkt->size;
+ q = avpkt->data;
+
if (avctx->width > 0 && avctx->height > 0) {
if (buf_size < 11)
return AVERROR_BUFFER_TOO_SMALL;
@@ -301,7 +310,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 +322,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 +362,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 +381,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 +410,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 +425,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 +436,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 +472,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 +511,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 +523,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 943a7466d9..ff21e75ef3 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -20,6 +20,7 @@
*/
#include "avcodec.h"
#include "bytestream.h"
+#include "encode.h"
#include "internal.h"
#include "libavutil/avassert.h"
#include "libavutil/bprint.h"
@@ -114,15 +115,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 +132,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 +232,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 +248,57 @@ 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;
int x2, y2;
int forced = 0;
+ int outbuf_size;
+ int64_t duration_ms;
- 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 != AV_SUBTITLE_FMT_BITMAP) {
+ if (frame->subtitle_areas[i]->type != AV_SUBTITLE_FMT_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 +310,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);
}
@@ -335,6 +343,16 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
out_palette[i], out_alpha[i] >> 4);
av_log(avctx, AV_LOG_DEBUG, "\n");
+
+ ret = ff_get_encode_buffer(avctx, avpkt, 4 + vrect.w * vrect.h / 2 + 17 + 21, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
+
+ outbuf_size = avpkt->size;
+ outbuf = avpkt->data;
+
// encode data block
q = outbuf + 4;
offset1 = q - outbuf;
@@ -344,10 +362,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 +380,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, 0);
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 +412,8 @@ 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);
+ duration_ms = av_rescale_q(frame->subtitle_timing.duration, AV_TIME_BASE_Q, (AVRational){ 1, 1000 });
+ bytestream_put_be16(&q, (duration_ms*90) >> 10);
bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
*q++ = 0x02; // set end
*q++ = 0xff; // terminating command
@@ -403,7 +422,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 +488,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 +519,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 618be0573d..da195bb77a 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -140,17 +140,70 @@ fail:
return ret;
}
-int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
- const AVSubtitle *sub)
+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);
+ memset(buf, 0, buf_size);
+ // 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;
+ }
+
+ // Copy legacy subtitle data to temp frame
+ ret = ff_frame_put_subtitle(frame, sub);
+ if (ret < 0)
+ goto exit;
+
+ ret = avcodec_send_frame(avctx, frame);
+ if (ret < 0)
+ goto exit;
+
+ ret = avcodec_receive_packet(avctx, avpkt);
+
+ if (ret < 0 && ret != AVERROR(EAGAIN))
+ goto exit;
+
+ //ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
+
avctx->frame_number++;
+
+ if (avpkt->size) {
+ if (avpkt->size > buf_size) {
+ ret = AVERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ memcpy(buf, avpkt->data, avpkt->size);
+ ret = avpkt->size;
+ }
+
+exit:
+
+ av_packet_free(&avpkt);
+ av_frame_free(&frame);
return ret;
}
diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c
index d506ed5c37..6383d694a8 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -29,6 +29,7 @@
#include "libavutil/ass_split_internal.h"
#include "libavutil/ass_internal.h"
#include "bytestream.h"
+#include "encode.h"
#include "internal.h"
#define STYLE_FLAG_BOLD (1<<0)
@@ -73,6 +74,7 @@ typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
ASSStyle *ass_dialog_style;
StyleBox *style_attributes;
unsigned count;
@@ -329,7 +331,7 @@ 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);
+ s->ass_ctx = avpriv_ass_split((char*)avctx->subtitle_header);
if (!s->ass_ctx)
return AVERROR_INVALIDDATA;
ret = encode_sample_description(avctx);
@@ -633,56 +635,112 @@ 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;
+
+ ensure_ass_context(avctx, frame);
s->text_pos = 0;
s->count = 0;
s->box_flags = 0;
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);
- mov_text_dialog(s, dialog);
- avpriv_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
- avpriv_ass_free_dialog(&dialog);
+ if (ass) {
+
+ if (i > 0)
+ mov_text_new_line_cb(s, 0);
+
+ 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 (s->buffer.len > UINT16_MAX)
- return AVERROR(ERANGE);
- AV_WB16(buf, s->buffer.len);
- buf += 2;
+ if (!av_bprint_is_complete(&s->buffer)) {
+ return AVERROR(ENOMEM);
+ }
- for (size_t j = 0; j < box_count; j++)
+ for (j = 0; j < box_count; j++) {
box_types[j].encode(s);
+ }
- if (!av_bprint_is_complete(&s->buffer))
- return AVERROR(ENOMEM);
+ ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
- if (!s->buffer.len)
- return 0;
+ buf = avpkt->data;
- if (s->buffer.len > bufsize - 3) {
+ AV_WB16(buf, s->buffer.len);
+ buf += 2;
+
+ if (s->buffer.len > avpkt->size - 3) {
av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return 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;
- return length;
+exit:
+ return ret;
}
#define OFFSET(x) offsetof(MovTextContext, x)
@@ -707,7 +765,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..ebe42ef817 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -21,6 +21,7 @@
#include <stdarg.h>
#include "avcodec.h"
+#include "encode.h"
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "libavutil/ass_split_internal.h"
@@ -33,6 +34,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 +132,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 +228,95 @@ 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;
+ int i, ret;
+
+ 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) {
+
+ if (i > 0)
+ srt_new_line_cb(s, 0);
+
+ 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) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
}
- 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 +336,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 +350,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 +364,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..e6d8a01a73 100644
--- a/libavcodec/ttmlenc.c
+++ b/libavcodec/ttmlenc.c
@@ -33,11 +33,17 @@
#include "libavutil/bprint.h"
#include "libavutil/internal.h"
#include "libavutil/ass_split_internal.h"
+#include "libavutil/ass_internal.h"
#include "ttmlenc.h"
+#include "encode.h"
+
+
typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
+ int extradata_written;
AVBPrint buffer;
} TTMLContext;
@@ -76,28 +82,75 @@ 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;
+ int i, ret;
+
+ 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;
- int ret;
+ 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);
}
+ if (!ass)
+ continue;
+
dialog = avpriv_ass_split_dialog(s->ass_ctx, ass);
if (!dialog)
return AVERROR(ENOMEM);
+ if (i > 0)
+ ttml_new_line_cb(s, 0);
+
if (dialog->style) {
av_bprintf(&s->buffer, "<span region=\"");
av_bprint_escape(&s->buffer, dialog->style, NULL,
@@ -130,17 +183,19 @@ 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) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+
+ ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 3, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
}
- return s->buffer.len;
+ av_strlcpy(avpkt->data, s->buffer.str, avpkt->size);
+
+ avpkt->size = s->buffer.len;
+ *got_packet = 1;
+
+ return 0;
}
static av_cold int ttml_encode_close(AVCodecContext *avctx)
@@ -370,13 +425,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 +444,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..c0436f5739 100644
--- a/libavcodec/webvttenc.c
+++ b/libavcodec/webvttenc.c
@@ -22,6 +22,7 @@
#include <stdarg.h>
#include "avcodec.h"
+#include "encode.h"
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "libavutil/ass_split_internal.h"
@@ -32,6 +33,7 @@
typedef struct {
AVCodecContext *avctx;
ASSSplitContext *ass_ctx;
+ int is_default_ass_context;
AVBPrint buffer;
unsigned timestamp_end;
int count;
@@ -155,43 +157,81 @@ 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;
+ int ret, 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) {
+
+ if (i > 0)
+ webvtt_new_line_cb(s, 0);
+
+ 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) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ ret = ff_get_encode_buffer(avctx, avpkt, s->buffer.len + 1, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
}
- 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 = s->buffer.len > 0;
+
+ return 0;
}
static int webvtt_encode_close(AVCodecContext *avctx)
@@ -206,9 +246,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 +258,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..ef804f21f4 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -22,6 +22,7 @@
#include "avcodec.h"
#include "bytestream.h"
+#include "encode.h"
#include "internal.h"
#include "put_bits.h"
@@ -111,39 +112,56 @@ 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 int64_t duration_ms = (int64_t)((double)frame->subtitle_timing.duration * av_q2d(AV_TIME_BASE_Q) * 1000);
+ const uint64_t startTime = (int64_t)((double)frame->subtitle_timing.start_pts * av_q2d(AV_TIME_BASE_Q) * 1000);
+ const uint64_t endTime = startTime + duration_ms;
int start_tc[4], end_tc[4];
- uint8_t *hdr = buf + 27; // Point behind the timestamp
+ uint8_t *hdr;
uint8_t *rlelenptr;
uint16_t width, height;
- int i;
+ int i, ret;
PutBitContext pb;
+ uint8_t* buf;
+ int64_t req_size;
- if (bufsize < 27 + 7*2 + 4*3) {
- av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
- return AVERROR_BUFFER_TOO_SMALL;
+ if (!frame->num_subtitle_areas) {
+ // Don't encode empty sub events
+ return 0;
}
+ // Estimate size (timestamp 27, header 7*2 + 4*3, padding 10)
+ req_size = 27 + 7*2 + 4*3 + 10;
+ req_size += frame->subtitle_areas[0]->linesize[0] * frame->subtitle_areas[0]->h;
+ req_size += 256; // Palette
+
+ ret = ff_get_encode_buffer(avctx, avpkt, req_size, 0);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ return ret;
+ }
+
+ buf = avpkt->data;
+ hdr = avpkt->data + 27; // Point behind the timestamp
+
// 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 +169,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 +178,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 +237,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,
};
--
ffmpeg-codebot
More information about the ffmpeg-devel
mailing list