[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