[FFmpeg-devel] [PATCH v15 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 02:48:48 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        |  97 +++++++++++++++++++++++++++++++++-
 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, 540 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..40b8a8e278 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -140,16 +140,109 @@ 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, got_packet = 0;
+    AVFrame *frame;
+    AVPacket* avpkt;
+
     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->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_frame_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