diff mbox series

[FFmpeg-devel,v9,23/25] avcodec/subtitles: Migrate subtitle encoders to frame-based API

Message ID 300c14faaa3e95ec6f2785a0099d815264ecc9a7.1666689226.git.ffmpegagent@gmail.com
State New
Headers show
Series Subtitle Filtering 2022 | expand

Checks

Context Check Description
yinshiyou/make_fate_loongarch64 success Make fate finished
yinshiyou/make_loongarch64 warning New warnings during build

Commit Message

Aman Karmani Oct. 25, 2022, 9:13 a.m. UTC
From: softworkz <softworkz@hotmail.com>

and provide a compatibility shim for the legacy api

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/assenc.c         | 189 ++++++++++++++++++++++++++++++------
 libavcodec/avcodec.h        |   5 +-
 libavcodec/codec_internal.h |  12 ---
 libavcodec/dvbsubenc.c      |  96 ++++++++++--------
 libavcodec/dvdsubenc.c      | 103 ++++++++++++--------
 libavcodec/encode.c         |  61 +++++++++++-
 libavcodec/movtextenc.c     | 114 ++++++++++++++++------
 libavcodec/srtenc.c         | 108 ++++++++++++++-------
 libavcodec/tests/avcodec.c  |   5 +-
 libavcodec/ttmlenc.c        | 101 ++++++++++++++-----
 libavcodec/utils.c          |   1 -
 libavcodec/webvttenc.c      |  86 +++++++++++-----
 libavcodec/xsubenc.c        |  88 ++++++++++-------
 13 files changed, 689 insertions(+), 280 deletions(-)

Comments

Michael Niedermayer Oct. 27, 2022, 5:53 p.m. UTC | #1
On Tue, Oct 25, 2022 at 09:13:44AM +0000, softworkz wrote:
> From: softworkz <softworkz@hotmail.com>
> 
> and provide a compatibility shim for the legacy api
> 
> Signed-off-by: softworkz <softworkz@hotmail.com>
> ---
>  libavcodec/assenc.c         | 189 ++++++++++++++++++++++++++++++------
>  libavcodec/avcodec.h        |   5 +-
>  libavcodec/codec_internal.h |  12 ---
>  libavcodec/dvbsubenc.c      |  96 ++++++++++--------
>  libavcodec/dvdsubenc.c      | 103 ++++++++++++--------
>  libavcodec/encode.c         |  61 +++++++++++-
>  libavcodec/movtextenc.c     | 114 ++++++++++++++++------
>  libavcodec/srtenc.c         | 108 ++++++++++++++-------
>  libavcodec/tests/avcodec.c  |   5 +-
>  libavcodec/ttmlenc.c        | 101 ++++++++++++++-----
>  libavcodec/utils.c          |   1 -
>  libavcodec/webvttenc.c      |  86 +++++++++++-----
>  libavcodec/xsubenc.c        |  88 ++++++++++-------
>  13 files changed, 689 insertions(+), 280 deletions(-)

Causes this testcase to fail:
./ffmpeg -i 'bgc.sub.dub.ogm'  -vframes 3 -bitexact -y nosubs.webm

https://samples.ffmpeg.org/ogg/

I did not investgate why or if this a bug or expected. Just reporting a difference
ive seen

thx

[...]
Soft Works Oct. 27, 2022, 7:05 p.m. UTC | #2
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Michael Niedermayer
> Sent: Thursday, October 27, 2022 7:54 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v9 23/25] avcodec/subtitles:
> Migrate subtitle encoders to frame-based API
> 
> On Tue, Oct 25, 2022 at 09:13:44AM +0000, softworkz wrote:
> > From: softworkz <softworkz@hotmail.com>
> >
> > and provide a compatibility shim for the legacy api
> >
> > Signed-off-by: softworkz <softworkz@hotmail.com>
> > ---
> >  libavcodec/assenc.c         | 189 ++++++++++++++++++++++++++++++--
> ----
> >  libavcodec/avcodec.h        |   5 +-
> >  libavcodec/codec_internal.h |  12 ---
> >  libavcodec/dvbsubenc.c      |  96 ++++++++++--------
> >  libavcodec/dvdsubenc.c      | 103 ++++++++++++--------
> >  libavcodec/encode.c         |  61 +++++++++++-
> >  libavcodec/movtextenc.c     | 114 ++++++++++++++++------
> >  libavcodec/srtenc.c         | 108 ++++++++++++++-------
> >  libavcodec/tests/avcodec.c  |   5 +-
> >  libavcodec/ttmlenc.c        | 101 ++++++++++++++-----
> >  libavcodec/utils.c          |   1 -
> >  libavcodec/webvttenc.c      |  86 +++++++++++-----
> >  libavcodec/xsubenc.c        |  88 ++++++++++-------
> >  13 files changed, 689 insertions(+), 280 deletions(-)
> 
> Causes this testcase to fail:
> ./ffmpeg -i 'bgc.sub.dub.ogm'  -vframes 3 -bitexact -y nosubs.webm
> 
> https://samples.ffmpeg.org/ogg/
> 
> I did not investgate why or if this a bug or expected. Just reporting
> a difference
> ive seen

Hi Michael,

thanks a lot for testing my patchset! 

I have run the command with and without my patchset and I can confirm
that the output is different.

Though, from analyzing the output files, it appears that the output
with my patchset applied seems "more correct" than the output that ffmpeg
is currently creating.

The details of the investigation can be found here:
https://github.com/softworkz/SubtitleFilteringDemos/issues/2


Thanks again,
softworkz
diff mbox series

Patch

diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c
index 9bbcdd9054..45fd5a217f 100644
--- a/libavcodec/assenc.c
+++ b/libavcodec/assenc.c
@@ -25,67 +25,192 @@ 
 
 #include "avcodec.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "libavutil/ass_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 FFCodec ff_ssa_encoder = {
-    .p.name       = "ssa",
-    CODEC_LONG_NAME("ASS (Advanced SubStation Alpha) subtitle"),
-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
-    .p.id         = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+    .p.name           = "ssa",
+    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .p.type           = AVMEDIA_TYPE_SUBTITLE,
+    .p.id             = AV_CODEC_ID_ASS,
+    .priv_data_size = sizeof(AssEncContext),
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
 };
 #endif
 
 #if CONFIG_ASS_ENCODER
 const FFCodec ff_ass_encoder = {
-    .p.name       = "ass",
-    CODEC_LONG_NAME("ASS (Advanced SubStation Alpha) subtitle"),
-    .p.type       = AVMEDIA_TYPE_SUBTITLE,
-    .p.id         = AV_CODEC_ID_ASS,
-    .init         = ass_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ass_encode_frame),
+    .p.name           = "ass",
+    .p.long_name      = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"),
+    .p.type           = AVMEDIA_TYPE_SUBTITLE,
+    .priv_data_size = sizeof(AssEncContext),
+    .p.id             = AV_CODEC_ID_ASS,
+    .init           = ass_encode_init,
+    .close          = ass_encode_close,
+    FF_CODEC_RECEIVE_PACKET_CB(ass_receive_packet),
 };
 #endif
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 68b588861f..c1be88fd11 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -3024,10 +3024,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/codec_internal.h b/libavcodec/codec_internal.h
index e3b77e6dea..1aa7ed151e 100644
--- a/libavcodec/codec_internal.h
+++ b/libavcodec/codec_internal.h
@@ -108,9 +108,6 @@  enum FFCodecType {
     /* The codec is an encoder using the encode callback;
      * audio and video codecs only. */
     FF_CODEC_CB_TYPE_ENCODE,
-    /* The codec is an encoder using the encode_sub callback;
-     * subtitle codecs only. */
-    FF_CODEC_CB_TYPE_ENCODE_SUB,
     /* The codec is an encoder using the receive_packet callback;
      * audio and video codecs only. */
     FF_CODEC_CB_TYPE_RECEIVE_PACKET,
@@ -213,12 +210,6 @@  typedef struct FFCodec {
          */
         int (*encode)(struct AVCodecContext *avctx, struct AVPacket *avpkt,
                       const struct AVFrame *frame, int *got_packet_ptr);
-        /**
-         * Encode subtitles to a raw buffer.
-         * cb is in this state if cb_type is FF_CODEC_CB_TYPE_ENCODE_SUB.
-         */
-        int (*encode_sub)(struct AVCodecContext *avctx, uint8_t *buf,
-                          int buf_size, const struct AVSubtitle *sub);
         /**
          * Encode API with decoupled frame/packet dataflow.
          * cb is in this state if cb_type is FF_CODEC_CB_TYPE_RECEIVE_PACKET.
@@ -307,9 +298,6 @@  typedef struct FFCodec {
 #define FF_CODEC_ENCODE_CB(func)                          \
     .cb_type           = FF_CODEC_CB_TYPE_ENCODE,         \
     .cb.encode         = (func)
-#define FF_CODEC_ENCODE_SUB_CB(func)                      \
-    .cb_type           = FF_CODEC_CB_TYPE_ENCODE_SUB,     \
-    .cb.encode_sub     = (func)
 #define FF_CODEC_RECEIVE_PACKET_CB(func)                  \
     .cb_type           = FF_CODEC_CB_TYPE_RECEIVE_PACKET, \
     .cb.receive_packet = (func)
diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index 822e3a5309..da6543443c 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -21,6 +21,7 @@ 
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 #include "libavutil/colorspace.h"
 
 typedef struct DVBSubtitleContext {
@@ -269,21 +270,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;
@@ -302,7 +311,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 */
@@ -314,30 +323,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 {
@@ -354,12 +363,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;
@@ -373,22 +382,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 {
@@ -402,8 +411,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 */
@@ -417,9 +426,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);
@@ -428,13 +437,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 {
@@ -464,19 +473,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;
@@ -503,7 +512,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 FFCodec ff_dvbsub_encoder = {
@@ -512,5 +524,5 @@  const FFCodec ff_dvbsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVB_SUBTITLE,
     .priv_data_size = sizeof(DVBSubtitleContext),
-    FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
+    FF_CODEC_ENCODE_CB(dvbsub_encode),
 };
diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
index 624e21c000..408ac938c9 100644
--- a/libavcodec/dvdsubenc.c
+++ b/libavcodec/dvdsubenc.c
@@ -22,6 +22,8 @@ 
 #include "bytestream.h"
 #include "codec_internal.h"
 #include "dvdsub.h"
+#include "encode.h"
+#include "internal.h"
 #include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
 #include "libavutil/imgutils.h"
@@ -115,15 +117,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++)
@@ -133,7 +134,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) {
@@ -233,13 +234,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++)
@@ -249,51 +250,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;
@@ -305,27 +312,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);
     }
 
@@ -336,6 +345,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;
@@ -345,10 +364,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)) {
@@ -363,7 +382,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];
@@ -401,7 +420,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
@@ -410,7 +430,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);
@@ -474,14 +496,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;
 }
 
@@ -506,7 +527,7 @@  const FFCodec ff_dvdsub_encoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_DVD_SUBTITLE,
     .init           = dvdsub_init,
-    FF_CODEC_ENCODE_SUB_CB(dvdsub_encode),
+    FF_CODEC_ENCODE_CB(dvdsub_encode),
     .p.priv_class   = &dvdsubenc_class,
     .priv_data_size = sizeof(DVDSubtitleContext),
 };
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index fbe2c97cd6..27785c0242 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -161,17 +161,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;
+    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 = ffcodec(avctx->codec)->cb.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 6f87e30904..2d5f0347a5 100644
--- a/libavcodec/movtextenc.c
+++ b/libavcodec/movtextenc.c
@@ -30,6 +30,7 @@ 
 #include "libavutil/ass_internal.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.h"
 
 #define STYLE_FLAG_BOLD         (1<<0)
 #define STYLE_FLAG_ITALIC       (1<<1)
@@ -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 FFCodec ff_movtext_encoder = {
     .priv_data_size = sizeof(MovTextContext),
     .p.priv_class   = &mov_text_encoder_class,
     .init           = mov_text_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(mov_text_encode_frame),
+    FF_CODEC_ENCODE_CB(mov_text_encode_frame),
     .close          = mov_text_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c
index 245e6a37c5..1fa1a2da26 100644
--- a/libavcodec/srtenc.c
+++ b/libavcodec/srtenc.c
@@ -23,6 +23,7 @@ 
 
 #include <stdarg.h>
 #include "avcodec.h"
+#include "encode.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "codec_internal.h"
@@ -35,6 +36,7 @@ 
 typedef struct {
     AVCodecContext *avctx;
     ASSSplitContext *ass_ctx;
+    int is_default_ass_context;
     AVBPrint buffer;
     char stack[SRT_STACK_SIZE];
     int stack_ptr;
@@ -132,14 +134,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)
@@ -229,58 +230,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)
@@ -300,7 +338,7 @@  const FFCodec ff_srt_encoder = {
     .p.id           = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+    FF_CODEC_ENCODE_CB(srt_encode_frame),
     .close          = srt_encode_close,
 };
 #endif
@@ -313,7 +351,7 @@  const FFCodec ff_subrip_encoder = {
     .p.id           = AV_CODEC_ID_SUBRIP,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(srt_encode_frame),
+    FF_CODEC_ENCODE_CB(srt_encode_frame),
     .close          = srt_encode_close,
 };
 #endif
@@ -326,7 +364,7 @@  const FFCodec ff_text_encoder = {
     .p.id           = AV_CODEC_ID_TEXT,
     .priv_data_size = sizeof(SRTContext),
     .init           = srt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(text_encode_frame),
+    FF_CODEC_ENCODE_CB(text_encode_frame),
     .close          = srt_encode_close,
 };
 #endif
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 3288a85f64..ed8b9212bb 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -109,7 +109,6 @@  int main(void){
             is_decoder = 1;
             break;
         case FF_CODEC_CB_TYPE_ENCODE:
-        case FF_CODEC_CB_TYPE_ENCODE_SUB:
         case FF_CODEC_CB_TYPE_RECEIVE_PACKET:
             is_encoder = 1;
             break;
@@ -125,15 +124,13 @@  int main(void){
 #define CHECK(TYPE, type) (codec2->cb_type == FF_CODEC_CB_TYPE_ ## TYPE && !codec2->cb.type)
         if (CHECK(DECODE, decode) || CHECK(DECODE_SUB, decode_sub) ||
             CHECK(RECEIVE_PACKET, receive_packet) ||
-            CHECK(ENCODE, encode) || CHECK(ENCODE_SUB, encode_sub) ||
+            CHECK(ENCODE, encode) ||
             CHECK(RECEIVE_FRAME, receive_frame)) {
             ERR_EXT("Codec %s does not implement its %s callback.\n",
                     is_decoder ? "decoding" : "encoding");
         }
 #undef CHECK
         if (is_encoder) {
-            if ((codec->type == AVMEDIA_TYPE_SUBTITLE) != (codec2->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB))
-                ERR("Encoder %s is both subtitle encoder and not subtitle encoder.");
             if (codec2->update_thread_context || codec2->update_thread_context_for_user || codec2->bsfs)
                 ERR("Encoder %s has decoder-only thread functions or bsf.\n");
             if (codec->type == AVMEDIA_TYPE_AUDIO) {
diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c
index c3f873147c..128b50c53a 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 FFCodec ff_ttml_encoder = {
     .p.id           = AV_CODEC_ID_TTML,
     .priv_data_size = sizeof(TTMLContext),
     .init           = ttml_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(ttml_encode_frame),
+    FF_CODEC_ENCODE_CB(ttml_encode_frame),
     .close          = ttml_encode_close,
     .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 552cdff70f..3c88952f2b 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -76,7 +76,6 @@  int av_codec_is_encoder(const AVCodec *avcodec)
 {
     const FFCodec *const codec = ffcodec(avcodec);
     return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||
-                     codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||
                      codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
 }
 
diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c
index 2df31f8fba..a21a922ef0 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 "codec_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) {
+        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 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 FFCodec ff_webvtt_encoder = {
@@ -218,6 +258,6 @@  const FFCodec ff_webvtt_encoder = {
     .p.id           = AV_CODEC_ID_WEBVTT,
     .priv_data_size = sizeof(WebVTTContext),
     .init           = webvtt_encode_init,
-    FF_CODEC_ENCODE_SUB_CB(webvtt_encode_frame),
+    FF_CODEC_ENCODE_CB(webvtt_encode_frame),
     .close          = webvtt_encode_close,
 };
diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c
index 6f417c5958..10147bae8e 100644
--- a/libavcodec/xsubenc.c
+++ b/libavcodec/xsubenc.c
@@ -23,6 +23,7 @@ 
 #include "avcodec.h"
 #include "bytestream.h"
 #include "codec_internal.h"
+#include "encode.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,5 +237,5 @@  const FFCodec ff_xsub_encoder = {
     .p.type     = AVMEDIA_TYPE_SUBTITLE,
     .p.id       = AV_CODEC_ID_XSUB,
     .init       = xsub_encoder_init,
-    FF_CODEC_ENCODE_SUB_CB(xsub_encode),
+    FF_CODEC_ENCODE_CB(xsub_encode),
 };