diff mbox series

[FFmpeg-devel,v16,15/16] avcodec/subtitles: Migrate subtitle encoders to frame-based API and provide a compatibility shim for the legacy api

Message ID DM8P223MB0365F26EEC3AEA0A60CEDE9DBA629@DM8P223MB0365.NAMP223.PROD.OUTLOOK.COM
State Superseded, archived
Headers show
Series [FFmpeg-devel,v16,01/16] global: Prepare AVFrame for subtitle handling | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/makex86 warning New warnings during build
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished
andriy/makeppc warning New warnings during build

Commit Message

Soft Works Nov. 25, 2021, 5:54 p.m. UTC
Also introduce deferred loading of ass headers for all cases where it can't be taken from the context of a decoder.

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavcodec/assenc.c        |  81 ++++++++++++++++++++---------
 libavcodec/avcodec.h       |   7 +++
 libavcodec/dvbsubenc.c     |  85 +++++++++++++++---------------
 libavcodec/dvdsubenc.c     |  89 +++++++++++++++++---------------
 libavcodec/encode.c        |  98 ++++++++++++++++++++++++++++++++++-
 libavcodec/movtextenc.c    | 103 +++++++++++++++++++++++++------------
 libavcodec/srtenc.c        |  96 ++++++++++++++++++++++------------
 libavcodec/tests/avcodec.c |   2 -
 libavcodec/ttmlenc.c       |  82 +++++++++++++++++++++++------
 libavcodec/webvttenc.c     |  73 +++++++++++++++++++-------
 libavcodec/xsubenc.c       |  65 ++++++++++++-----------
 11 files changed, 541 insertions(+), 240 deletions(-)

Comments

Andreas Rheinhardt Nov. 26, 2021, 3:09 p.m. UTC | #1
Soft Works:
> Also introduce deferred loading of ass headers for all cases where it can't be taken from the context of a decoder.

This should be a commit of its own.

> 
> Signed-off-by: softworkz <softworkz@hotmail.com>
> ---
>  libavcodec/assenc.c        |  81 ++++++++++++++++++++---------
>  libavcodec/avcodec.h       |   7 +++
>  libavcodec/dvbsubenc.c     |  85 +++++++++++++++---------------
>  libavcodec/dvdsubenc.c     |  89 +++++++++++++++++---------------
>  libavcodec/encode.c        |  98 ++++++++++++++++++++++++++++++++++-
>  libavcodec/movtextenc.c    | 103 +++++++++++++++++++++++++------------
>  libavcodec/srtenc.c        |  96 ++++++++++++++++++++++------------
>  libavcodec/tests/avcodec.c |   2 -
>  libavcodec/ttmlenc.c       |  82 +++++++++++++++++++++++------
>  libavcodec/webvttenc.c     |  73 +++++++++++++++++++-------
>  libavcodec/xsubenc.c       |  65 ++++++++++++-----------
>  11 files changed, 541 insertions(+), 240 deletions(-)
> 
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 0c5819b116..304b35ba86 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -2949,10 +2949,17 @@ void av_parser_close(AVCodecParserContext *s);
>   * @{
>   */
>  
> + /**
> +  * @deprecated Use @ref avcodec_encode_subtitle2() instead.
> +  */
> +attribute_deprecated
>  int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
>                              const AVSubtitle *sub);
>  
>  
> +int avcodec_encode_subtitle2(AVCodecContext* avctx, struct AVPacket* avpkt,
> +    AVFrame* frame, int* got_packet);
> +

Missing documentation. The signature and the implementation shows that
you have recreated the old encode API avcodec_encode_(audio2|video2)
with the difference that you (like the current subtitle API) require
preallocated (not necessarily refcounted) buffers in avpkt. Why not use
the ordinary encode API?
(Why is frame not const?)
Apart from that: You never zero the packet padding. I don't even know
whether you expect the buffer to be padded.

>  /**
>   * @}
>   */
> diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c
> index ff4fbed39d..74fefe5664 100644
> --- a/libavcodec/dvdsubenc.c
> +++ b/libavcodec/dvdsubenc.c
> @@ -114,15 +114,14 @@ static int color_distance(uint32_t a, uint32_t b)
>   * Count colors used in a rectangle, quantizing alpha and grouping by
>   * nearest global palette entry.
>   */
> -static void count_colors(AVCodecContext *avctx, unsigned hits[33],
> -                         const AVSubtitleRect *r)
> +static void count_colors(const AVCodecContext *avctx, unsigned hits[33],
> +                         const AVSubtitleArea *r)
>  {
>      DVDSubtitleContext *dvdc = avctx->priv_data;
>      unsigned count[256] = { 0 };
> -    uint32_t *palette = (uint32_t *)r->data[1];
>      uint32_t color;
>      int x, y, i, j, match, d, best_d, av_uninit(best_j);
> -    uint8_t *p = r->data[0];
> +    uint8_t *p = r->buf[0]->data;
>  
>      for (y = 0; y < r->h; y++) {
>          for (x = 0; x < r->w; x++)
> @@ -132,7 +131,7 @@ static void count_colors(AVCodecContext *avctx, unsigned hits[33],
>      for (i = 0; i < 256; i++) {
>          if (!count[i]) /* avoid useless search */
>              continue;
> -        color = palette[i];
> +        color = r->pal[i];
>          /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
>          match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
>          if (match) {
> @@ -232,13 +231,13 @@ static void build_color_map(AVCodecContext *avctx, int cmap[],
>      }
>  }
>  
> -static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
> +static void copy_rectangle(AVSubtitleArea*dst, AVSubtitleArea *src, int cmap[])
>  {
>      int x, y;
>      uint8_t *p, *q;
>  
> -    p = src->data[0];
> -    q = dst->data[0] + (src->x - dst->x) +
> +    p = src->buf[0]->data;
> +    q = dst->buf[0]->data + (src->x - dst->x) +
>                              (src->y - dst->y) * dst->linesize[0];
>      for (y = 0; y < src->h; y++) {
>          for (x = 0; x < src->w; x++)
> @@ -248,51 +247,56 @@ static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
>      }
>  }
>  
> -static int encode_dvd_subtitles(AVCodecContext *avctx,
> -                                uint8_t *outbuf, int outbuf_size,
> -                                const AVSubtitle *h)
> +static int encode_dvd_subtitles(AVCodecContext* avctx, AVPacket* avpkt,
> +                                const AVFrame* frame, int* got_packet)
>  {
>      DVDSubtitleContext *dvdc = avctx->priv_data;
>      uint8_t *q, *qq;
>      int offset1, offset2;
> -    int i, rects = h->num_rects, ret;
> +    int ret = 0;
> +    unsigned i, rects = frame->num_subtitle_areas;
>      unsigned global_palette_hits[33] = { 0 };
>      int cmap[256];
>      int out_palette[4];
>      int out_alpha[4];
> -    AVSubtitleRect vrect;
> -    uint8_t *vrect_data = NULL;
> +    AVSubtitleArea vrect;

Ok, seems like sizeof(AVSubtitleArea) is part of the API/ABI.

> +    uint8_t* vrect_data = NULL, *outbuf = avpkt->data;
>      int x2, y2;
>      int forced = 0;
> +    int outbuf_size = avpkt->size;
>  
> -    if (rects == 0 || !h->rects)
> +    if (rects == 0)
> +        return 0;
> +
> +    if (!frame->subtitle_areas)
>          return AVERROR(EINVAL);
> +
>      for (i = 0; i < rects; i++)
> -        if (h->rects[i]->type != SUBTITLE_BITMAP) {
> +        if (frame->subtitle_areas[i]->type != SUBTITLE_BITMAP) {
>              av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
>              return AVERROR(EINVAL);
>          }
>      /* Mark this subtitle forced if any of the rectangles is forced. */
>      for (i = 0; i < rects; i++)
> -        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
> +        if ((frame->subtitle_areas[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
>              forced = 1;
>              break;
>          }
>  
> -    vrect = *h->rects[0];
> +    vrect = *frame->subtitle_areas[0];
>  
>      if (rects > 1) {
>          /* DVD subtitles can have only one rectangle: build a virtual
>             rectangle containing all actual rectangles.
>             The data of the rectangles will be copied later, when the palette
>             is decided, because the rectangles may have different palettes. */
> -        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
> -        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
> +        int xmin = frame->subtitle_areas[0]->x, xmax = xmin + frame->subtitle_areas[0]->w;
> +        int ymin = frame->subtitle_areas[0]->y, ymax = ymin + frame->subtitle_areas[0]->h;
>          for (i = 1; i < rects; i++) {
> -            xmin = FFMIN(xmin, h->rects[i]->x);
> -            ymin = FFMIN(ymin, h->rects[i]->y);
> -            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
> -            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
> +            xmin = FFMIN(xmin, frame->subtitle_areas[i]->x);
> +            ymin = FFMIN(ymin, frame->subtitle_areas[i]->y);
> +            xmax = FFMAX(xmax, frame->subtitle_areas[i]->x + frame->subtitle_areas[i]->w);
> +            ymax = FFMAX(ymax, frame->subtitle_areas[i]->y + frame->subtitle_areas[i]->h);
>          }
>          vrect.x = xmin;
>          vrect.y = ymin;
> @@ -304,27 +308,29 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
>          /* Count pixels outside the virtual rectangle as transparent */
>          global_palette_hits[0] = vrect.w * vrect.h;
>          for (i = 0; i < rects; i++)
> -            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
> +            global_palette_hits[0] -= frame->subtitle_areas[i]->w * frame->subtitle_areas[i]->h;
>      }
>  
>      for (i = 0; i < rects; i++)
> -        count_colors(avctx, global_palette_hits, h->rects[i]);
> +        count_colors(avctx, global_palette_hits, frame->subtitle_areas[i]);
>      select_palette(avctx, out_palette, out_alpha, global_palette_hits);
>  
>      if (rects > 1) {
> -        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
> +
> +        vrect.buf[0] = av_buffer_allocz((size_t)vrect.w * vrect.h);

You are using the AVBuffer API here although this buffer will never be
shared with anything. This can and should be avoided (and anyway: you
did not remove vrect_data. The av_free(vrect_data) below is an
av_free(NULL). And in case one goes to fail

> +        if (!vrect.buf[0])
>              return AVERROR(ENOMEM);
> -        vrect.data    [0] = vrect_data;
> +
>          vrect.linesize[0] = vrect.w;
>          for (i = 0; i < rects; i++) {
> -            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
> +            build_color_map(avctx, cmap, frame->subtitle_areas[i]->pal,
>                              out_palette, out_alpha);
> -            copy_rectangle(&vrect, h->rects[i], cmap);
> +            copy_rectangle(&vrect, frame->subtitle_areas[i], cmap);
>          }
>          for (i = 0; i < 4; i++)
>              cmap[i] = i;
>      } else {
> -        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
> +        build_color_map(avctx, cmap, frame->subtitle_areas[0]->pal,
>                          out_palette, out_alpha);
>      }
>  
> @@ -344,10 +350,10 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
>          ret = AVERROR_BUFFER_TOO_SMALL;
>          goto fail;
>      }
> -    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
> +    dvd_encode_rle(&q, vrect.buf[0]->data, vrect.w * 2,
>                     vrect.w, (vrect.h + 1) >> 1, cmap);
>      offset2 = q - outbuf;
> -    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
> +    dvd_encode_rle(&q, vrect.buf[0]->data + vrect.w, vrect.w * 2,
>                     vrect.w, vrect.h >> 1, cmap);
>  
>      if (dvdc->even_rows_fix && (vrect.h & 1)) {
> @@ -362,7 +368,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
>      bytestream_put_be16(&qq, q - outbuf);
>  
>      // send start display command
> -    bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
> +    bytestream_put_be16(&q, (frame->subtitle_start_time * 90) >> 10);
>      bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2);
>      *q++ = 0x03; // palette - 4 nibbles
>      *q++ = (out_palette[3] << 4) | out_palette[2];
> @@ -394,7 +400,7 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
>      *q++ = 0xff; // terminating command
>  
>      // send stop display command last
> -    bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
> +    bytestream_put_be16(&q, (frame->subtitle_end_time*90) >> 10);
>      bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
>      *q++ = 0x02; // set end
>      *q++ = 0xff; // terminating command
> @@ -403,7 +409,9 @@ static int encode_dvd_subtitles(AVCodecContext *avctx,
>      bytestream_put_be16(&qq, q - outbuf);
>  
>      av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%"PTRDIFF_SPECIFIER"\n", q - outbuf);
> -    ret = q - outbuf;
> +    avpkt->size = q - outbuf;
> +    ret = 0;
> +    *got_packet = 1;
>  
>  fail:
>      av_free(vrect_data);

vrect.buf[0] leaks here in case rects > 1.

> @@ -467,14 +475,13 @@ static int dvdsub_init(AVCodecContext *avctx)
>      return 0;
>  }
>  
> -static int dvdsub_encode(AVCodecContext *avctx,
> -                         unsigned char *buf, int buf_size,
> -                         const AVSubtitle *sub)
> +static int dvdsub_encode(struct AVCodecContext* avctx, struct AVPacket* avpkt,
> +                         const struct AVFrame* frame, int* got_packet)
>  {
>      //DVDSubtitleContext *s = avctx->priv_data;
>      int ret;
>  
> -    ret = encode_dvd_subtitles(avctx, buf, buf_size, sub);
> +    ret = encode_dvd_subtitles(avctx, avpkt, frame, got_packet);
>      return ret;
>  }
>  
> @@ -499,7 +506,7 @@ const AVCodec ff_dvdsub_encoder = {
>      .type           = AVMEDIA_TYPE_SUBTITLE,
>      .id             = AV_CODEC_ID_DVD_SUBTITLE,
>      .init           = dvdsub_init,
> -    .encode_sub     = dvdsub_encode,
> +    .encode2        = dvdsub_encode,
>      .priv_class     = &dvdsubenc_class,
>      .priv_data_size = sizeof(DVDSubtitleContext),
>      .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
> diff --git a/libavcodec/encode.c b/libavcodec/encode.c
> index dd25cf999b..dfbec01a3d 100644
> --- a/libavcodec/encode.c
> +++ b/libavcodec/encode.c
> @@ -140,16 +140,110 @@ fail:
>      return ret;
>  }
>  
> +/**
> + * \brief 
> + * \param avctx 
> + * \param buf q
> + * \param buf_size 
> + * \param sub 
> + * \return 
> + */
>  int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
>                              const AVSubtitle *sub)
>  {
> -    int ret;
> +    int ret = 0, got_packet = 0;
> +    AVFrame *frame = NULL;
> +    AVPacket* avpkt = NULL;
> +
>      if (sub->start_display_time) {
>          av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
>          return -1;
>      }
>  
> -    ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub);
> +    // If the encoder implements the old API (encode_sub), call it directly:

You ported all subtitle encoders at once; this has the downside of
making the diff bigger and reviewing harder, but the advantage of
simplifying the compatibility code, because one knows that one does not
need to care about the case of an encoder implementing the old API. Yet
weirdly you didn't take advantage of this. You also did not remove
AVCodec.encode_sub.

> +    if (avctx->codec->encode_sub) {
> +        ret = avctx->codec->encode_sub(avctx, buf, buf_size, sub);
> +
> +        avctx->frame_number++;
> +        return ret;
> +    }
> +
> +    // Create a temporary frame for calling the regular api:
> +    frame = av_frame_alloc();

avcodec_open2() allocates two frames and three packets which are unused
when encoding subtitles. So you can reuse them here.

> +    if (!frame) {
> +        ret = AVERROR(ENOMEM);
> +        goto exit;
> +    }
> +
> +    frame->format = sub->format;
> +    frame->type = AVMEDIA_TYPE_SUBTITLE;
> +    ret = av_frame_get_buffer2(frame, 0);
> +    if (ret < 0)
> +        goto exit;
> +
> +    // Create a temporary packet
> +    avpkt = av_packet_alloc();
> +    if (!avpkt) {
> +        ret = AVERROR(ENOMEM);
> +        goto exit;
> +    }
> +
> +    avpkt->data = buf;
> +    avpkt->size = buf_size;
> +
> +    // Copy legacy subtitle data to temp frame
> +    av_frame_put_subtitle(frame, sub);
> +
> +    ret = avcodec_encode_subtitle2(avctx, avpkt, frame, &got_packet);
> +
> +    if (got_packet)
> +        ret = avpkt->size;
> +
> +    avpkt->data = NULL;
> +
> +exit:
> +
> +    av_packet_free(&avpkt);
> +    av_frame_free(&frame);
> +    return ret;
> +}
> +
> +int avcodec_encode_subtitle2(AVCodecContext *avctx, AVPacket* avpkt,
> +                             AVFrame* frame, int* got_packet)
> +{
> +    int ret;
> +
> +    *got_packet = 0;
> +    if (frame->subtitle_start_time) {
> +        av_log(avctx, AV_LOG_ERROR, "start_display_time must be 0.\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    if (avctx->codec->encode2) {
> +
> +        // Encoder implements the new/regular API        
> +        ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet);
> +    }
> +    else {
> +
> +        // Encoder implements the legacy API for subtitle encoding (encode_sub)
> +        // copy subtitle data to a temporary AVSubtitle (legacy) buffer
> +        AVSubtitle out_sub = { 0 };
> +        av_frame_get_subtitle(&out_sub, frame);
> +
> +        ret = avctx->codec->encode_sub(avctx, avpkt->data, avpkt->size, &out_sub);
> +
> +        if (ret >= 0) {
> +            avpkt->size = ret;
> +            ret = 0;
> +            *got_packet = 1;
> +        }
> +        else
> +            avpkt->size = 0;
> +
> +        avsubtitle_free(&out_sub);
> +    }
> +
>      avctx->frame_number++;
>      return ret;
>  }
Hendrik Leppkes Nov. 26, 2021, 3:37 p.m. UTC | #2
On Fri, Nov 26, 2021 at 4:09 PM Andreas Rheinhardt
<andreas.rheinhardt@outlook.com> wrote:
>
> Soft Works:
> > Also introduce deferred loading of ass headers for all cases where it can't be taken from the context of a decoder.
>
> This should be a commit of its own.
>
> >
> > Signed-off-by: softworkz <softworkz@hotmail.com>
> > ---
> >  libavcodec/assenc.c        |  81 ++++++++++++++++++++---------
> >  libavcodec/avcodec.h       |   7 +++
> >  libavcodec/dvbsubenc.c     |  85 +++++++++++++++---------------
> >  libavcodec/dvdsubenc.c     |  89 +++++++++++++++++---------------
> >  libavcodec/encode.c        |  98 ++++++++++++++++++++++++++++++++++-
> >  libavcodec/movtextenc.c    | 103 +++++++++++++++++++++++++------------
> >  libavcodec/srtenc.c        |  96 ++++++++++++++++++++++------------
> >  libavcodec/tests/avcodec.c |   2 -
> >  libavcodec/ttmlenc.c       |  82 +++++++++++++++++++++++------
> >  libavcodec/webvttenc.c     |  73 +++++++++++++++++++-------
> >  libavcodec/xsubenc.c       |  65 ++++++++++++-----------
> >  11 files changed, 541 insertions(+), 240 deletions(-)
> >
> > diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> > index 0c5819b116..304b35ba86 100644
> > --- a/libavcodec/avcodec.h
> > +++ b/libavcodec/avcodec.h
> > @@ -2949,10 +2949,17 @@ void av_parser_close(AVCodecParserContext *s);
> >   * @{
> >   */
> >
> > + /**
> > +  * @deprecated Use @ref avcodec_encode_subtitle2() instead.
> > +  */
> > +attribute_deprecated
> >  int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
> >                              const AVSubtitle *sub);
> >
> >
> > +int avcodec_encode_subtitle2(AVCodecContext* avctx, struct AVPacket* avpkt,
> > +    AVFrame* frame, int* got_packet);
> > +
>
> Missing documentation. The signature and the implementation shows that
> you have recreated the old encode API avcodec_encode_(audio2|video2)
> with the difference that you (like the current subtitle API) require
> preallocated (not necessarily refcounted) buffers in avpkt. Why not use
> the ordinary encode API?

I concur on that question. We have a generic encode/decode API without
dependency on any media type, that should be used going forward for
all media types, and not anything else being introduced now.

- Hendrik
Soft Works Nov. 27, 2021, 9:56 a.m. UTC | #3
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Andreas
> Rheinhardt
> Sent: Friday, November 26, 2021 4:09 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH v16 15/16] avcodec/subtitles: Migrate
> subtitle encoders to frame-based API and provide a compatibility shim for the
> legacy api
> 
> You ported all subtitle encoders at once; this has the downside of
> making the diff bigger and reviewing harder,

Nah, the changes are always the same, that's simple and straightforward.

> but the advantage of
> simplifying the compatibility code, because one knows that one does not
> need to care about the case of an encoder implementing the old API. Yet
> weirdly you didn't take advantage of this. You also did not remove
> AVCodec.encode_sub.

Are you saying I do not need to keep the compatibility layer for "legacy"
subtitle encoders?

I thought it's mandatory to keep it, but I'd be glad to remove..

softworkz
Andreas Rheinhardt Nov. 27, 2021, 10:04 a.m. UTC | #4
Soft Works:
> 
> 
>> -----Original Message-----
>> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Andreas
>> Rheinhardt
>> Sent: Friday, November 26, 2021 4:09 PM
>> To: ffmpeg-devel@ffmpeg.org
>> Subject: Re: [FFmpeg-devel] [PATCH v16 15/16] avcodec/subtitles: Migrate
>> subtitle encoders to frame-based API and provide a compatibility shim for the
>> legacy api
>>
>> You ported all subtitle encoders at once; this has the downside of
>> making the diff bigger and reviewing harder,
> 
> Nah, the changes are always the same, that's simple and straightforward.
> 

But they are in one big commit and that alone makes it harder. Of course
the combined diff is not bigger, but actually smaller (see below) when
porting everything in one commit.

>> but the advantage of
>> simplifying the compatibility code, because one knows that one does not
>> need to care about the case of an encoder implementing the old API. Yet
>> weirdly you didn't take advantage of this. You also did not remove
>> AVCodec.encode_sub.
> 
> Are you saying I do not need to keep the compatibility layer for "legacy"
> subtitle encoders?
> 
> I thought it's mandatory to keep it, but I'd be glad to remove..
> 

You need to keep the external API around for a while and this will force
some internal compatibility layer; but you also added unnecessary
internal compatibility layers: In both your new
avcodec_encode_subtitle2() as well as the old avcodec_encode_subtitle()
you do not need to care about what happens if the encoder implements the
old encode_sub API, because it no encoder implements said API any more.
You can (and should) remove AVCodec.encode_sub if you port all of its
users to encode2.

- Andreas
Soft Works Nov. 27, 2021, 10:17 a.m. UTC | #5
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Andreas
> Rheinhardt
> Sent: Saturday, November 27, 2021 11:04 AM
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH v16 15/16] avcodec/subtitles: Migrate
> subtitle encoders to frame-based API and provide a compatibility shim for the
> legacy api
> 
> Soft Works:
> >
> >
> >> -----Original Message-----
> >> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Andreas
> >> Rheinhardt
> >> Sent: Friday, November 26, 2021 4:09 PM
> >> To: ffmpeg-devel@ffmpeg.org
> >> Subject: Re: [FFmpeg-devel] [PATCH v16 15/16] avcodec/subtitles: Migrate
> >> subtitle encoders to frame-based API and provide a compatibility shim for
> the
> >> legacy api
> >>
> >> You ported all subtitle encoders at once; this has the downside of
> >> making the diff bigger and reviewing harder,
> >
> > Nah, the changes are always the same, that's simple and straightforward.
> >
> 
> But they are in one big commit and that alone makes it harder. Of course
> the combined diff is not bigger, but actually smaller (see below) when
> porting everything in one commit.
> 
> >> but the advantage of
> >> simplifying the compatibility code, because one knows that one does not
> >> need to care about the case of an encoder implementing the old API. Yet
> >> weirdly you didn't take advantage of this. You also did not remove
> >> AVCodec.encode_sub.
> >
> > Are you saying I do not need to keep the compatibility layer for "legacy"
> > subtitle encoders?
> >
> > I thought it's mandatory to keep it, but I'd be glad to remove..
> >
> 
> You need to keep the external API around for a while and this will force
> some internal compatibility layer; but you also added unnecessary
> internal compatibility layers: In both your new
> avcodec_encode_subtitle2() as well as the old avcodec_encode_subtitle()
> you do not need to care about what happens if the encoder implements the
> old encode_sub API, because it no encoder implements said API any more.

So there don't exist any external encoders that would need to be supported?
That's what I had thought.

softworkz
Andreas Rheinhardt Nov. 27, 2021, 10:27 a.m. UTC | #6
Soft Works:
> 
> 
>> -----Original Message-----
>> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Andreas
>> Rheinhardt
>> Sent: Saturday, November 27, 2021 11:04 AM
>> To: ffmpeg-devel@ffmpeg.org
>> Subject: Re: [FFmpeg-devel] [PATCH v16 15/16] avcodec/subtitles: Migrate
>> subtitle encoders to frame-based API and provide a compatibility shim for the
>> legacy api
>>
>> Soft Works:
>>>
>>>
>>>> -----Original Message-----
>>>> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Andreas
>>>> Rheinhardt
>>>> Sent: Friday, November 26, 2021 4:09 PM
>>>> To: ffmpeg-devel@ffmpeg.org
>>>> Subject: Re: [FFmpeg-devel] [PATCH v16 15/16] avcodec/subtitles: Migrate
>>>> subtitle encoders to frame-based API and provide a compatibility shim for
>> the
>>>> legacy api
>>>>
>>>> You ported all subtitle encoders at once; this has the downside of
>>>> making the diff bigger and reviewing harder,
>>>
>>> Nah, the changes are always the same, that's simple and straightforward.
>>>
>>
>> But they are in one big commit and that alone makes it harder. Of course
>> the combined diff is not bigger, but actually smaller (see below) when
>> porting everything in one commit.
>>
>>>> but the advantage of
>>>> simplifying the compatibility code, because one knows that one does not
>>>> need to care about the case of an encoder implementing the old API. Yet
>>>> weirdly you didn't take advantage of this. You also did not remove
>>>> AVCodec.encode_sub.
>>>
>>> Are you saying I do not need to keep the compatibility layer for "legacy"
>>> subtitle encoders?
>>>
>>> I thought it's mandatory to keep it, but I'd be glad to remove..
>>>
>>
>> You need to keep the external API around for a while and this will force
>> some internal compatibility layer; but you also added unnecessary
>> internal compatibility layers: In both your new
>> avcodec_encode_subtitle2() as well as the old avcodec_encode_subtitle()
>> you do not need to care about what happens if the encoder implements the
>> old encode_sub API, because it no encoder implements said API any more.
> 
> So there don't exist any external encoders that would need to be supported?
> That's what I had thought.
> 

Of course not. sizeof(AVCodec) is not part of the public API/ABI; to
quote from codec.h: "No fields below this line are part of the public
API. They may not be used outside of libavcodec and can be changed and
removed at will." All the function pointers are below that line and are
private, so you can't create an AVCodec of your own outside of lavc.
(Of course, you could do so in a fork, but you don't have to worry about
forks here.)

This is the same with e.g. AVFilter etc. The only exception is the
lavf-lavd mess.

- Andreas
diff mbox series

Patch

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