diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c
index 86d3e3e5a8..bf1260a947 100644
--- a/libavcodec/assdec.c
+++ b/libavcodec/assdec.c
@@ -40,11 +40,9 @@ static av_cold int ass_decode_init(AVCodecContext *avctx)
     return 0;
 }
 
-static int ass_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
-                            AVPacket *avpkt)
+static int ass_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                            int *got_sub_ptr, AVPacket *avpkt)
 {
-    AVSubtitle *sub = data;
-
     if (avpkt->size <= 0)
         return avpkt->size;
 
@@ -70,7 +68,7 @@ const FFCodec ff_ssa_decoder = {
     .p.type       = AVMEDIA_TYPE_SUBTITLE,
     .p.id         = AV_CODEC_ID_ASS,
     .init         = ass_decode_init,
-    .decode       = ass_decode_frame,
+    .decode_sub   = ass_decode_frame,
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
@@ -82,7 +80,7 @@ const FFCodec ff_ass_decoder = {
     .p.type       = AVMEDIA_TYPE_SUBTITLE,
     .p.id         = AV_CODEC_ID_ASS,
     .init         = ass_decode_init,
-    .decode       = ass_decode_frame,
+    .decode_sub   = ass_decode_frame,
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
 #endif
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index 371c8d18b7..32ee3a715d 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -838,10 +838,10 @@ static int process_cc608(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
     return ret;
 }
 
-static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avpkt)
+static int decode(AVCodecContext *avctx, AVSubtitle *sub,
+                  int *got_sub, AVPacket *avpkt)
 {
     CCaptionSubContext *ctx = avctx->priv_data;
-    AVSubtitle *sub = data;
     int64_t in_time = sub->pts;
     int64_t start_time;
     int64_t end_time;
@@ -955,6 +955,6 @@ const FFCodec ff_ccaption_decoder = {
     .init           = init_decoder,
     .close          = close_decoder,
     .flush          = flush_decoder,
-    .decode         = decode,
+    .decode_sub     = decode,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h
index 596cdbebd2..5c57c438f9 100644
--- a/libavcodec/codec_internal.h
+++ b/libavcodec/codec_internal.h
@@ -148,19 +148,24 @@ typedef struct FFCodec {
     int (*encode2)(struct AVCodecContext *avctx, struct AVPacket *avpkt,
                    const struct AVFrame *frame, int *got_packet_ptr);
     /**
-     * Decode picture or subtitle data.
+     * Decode picture data.
      *
      * @param      avctx          codec context
      * @param      outdata        codec type dependent output struct
      * @param[out] got_frame_ptr  decoder sets to 0 or 1 to indicate that a
-     *                            non-empty frame or subtitle was returned in
-     *                            outdata.
+     *                            non-empty frame was returned in outdata.
      * @param[in]  avpkt          AVPacket containing the data to be decoded
      * @return amount of bytes read from the packet on success, negative error
      *         code on failure
      */
     int (*decode)(struct AVCodecContext *avctx, void *outdata,
                   int *got_frame_ptr, struct AVPacket *avpkt);
+    /**
+     * Decode subtitle data. Same as decode except that it uses
+     * a struct AVSubtitle structure for output.
+     */
+    int (*decode_sub)(struct AVCodecContext *avctx, struct AVSubtitle *sub,
+                      int *got_frame_ptr, struct AVPacket *avpkt);
     int (*close)(struct AVCodecContext *);
     /**
      * Encode API with decoupled frame/packet dataflow. This function is called
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index f9fdb935f6..b5b78b9ca2 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -862,7 +862,7 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
         if (avctx->pkt_timebase.num && avpkt->pts != AV_NOPTS_VALUE)
             sub->pts = av_rescale_q(avpkt->pts,
                                     avctx->pkt_timebase, AV_TIME_BASE_Q);
-        ret = ffcodec(avctx->codec)->decode(avctx, sub, got_sub_ptr, pkt);
+        ret = ffcodec(avctx->codec)->decode_sub(avctx, sub, got_sub_ptr, pkt);
         if (pkt == avci->buffer_pkt) // did we recode?
             av_packet_unref(avci->buffer_pkt);
         if (ret < 0) {
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c
index 0731202aaf..c5391ea5ad 100644
--- a/libavcodec/dvbsubdec.c
+++ b/libavcodec/dvbsubdec.c
@@ -1607,14 +1607,12 @@ static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf,
     return 0;
 }
 
-static int dvbsub_decode(AVCodecContext *avctx,
-                         void *data, int *got_sub_ptr,
-                         AVPacket *avpkt)
+static int dvbsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
+                         int *got_sub_ptr, AVPacket *avpkt)
 {
     const uint8_t *buf = avpkt->data;
     int buf_size = avpkt->size;
     DVBSubContext *ctx = avctx->priv_data;
-    AVSubtitle *sub = data;
     const uint8_t *p, *p_end;
     int segment_type;
     int page_id;
@@ -1748,7 +1746,7 @@ const FFCodec ff_dvbsub_decoder = {
     .priv_data_size = sizeof(DVBSubContext),
     .init           = dvbsub_init_decoder,
     .close          = dvbsub_close_decoder,
-    .decode         = dvbsub_decode,
+    .decode_sub     = dvbsub_decode,
     .p.priv_class   = &dvbsubdec_class,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
index 734a387223..713170cdc2 100644
--- a/libavcodec/dvdsubdec.c
+++ b/libavcodec/dvdsubdec.c
@@ -548,14 +548,12 @@ static int append_to_cached_buf(AVCodecContext *avctx,
     return 0;
 }
 
-static int dvdsub_decode(AVCodecContext *avctx,
-                         void *data, int *data_size,
-                         AVPacket *avpkt)
+static int dvdsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
+                         int *data_size, AVPacket *avpkt)
 {
     DVDSubContext *ctx = avctx->priv_data;
     const uint8_t *buf = avpkt->data;
     int buf_size = avpkt->size;
-    AVSubtitle *sub = data;
     int appended = 0;
     int is_menu;
 
@@ -762,7 +760,7 @@ const FFCodec ff_dvdsub_decoder = {
     .p.id           = AV_CODEC_ID_DVD_SUBTITLE,
     .priv_data_size = sizeof(DVDSubContext),
     .init           = dvdsub_init,
-    .decode         = dvdsub_decode,
+    .decode_sub     = dvdsub_decode,
     .flush          = dvdsub_flush,
     .p.priv_class   = &dvdsub_class,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c
index 17a837ae70..0d8e601b28 100644
--- a/libavcodec/jacosubdec.c
+++ b/libavcodec/jacosubdec.c
@@ -162,11 +162,10 @@ static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src
     }
 }
 
-static int jacosub_decode_frame(AVCodecContext *avctx,
-                                void *data, int *got_sub_ptr, AVPacket *avpkt)
+static int jacosub_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                                int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret;
-    AVSubtitle *sub = data;
     const char *ptr = avpkt->data;
     FFASSDecoderContext *s = avctx->priv_data;
 
@@ -200,7 +199,7 @@ const FFCodec ff_jacosub_decoder = {
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_JACOSUB,
     .init           = ff_ass_subtitle_header_default,
-    .decode         = jacosub_decode_frame,
+    .decode_sub     = jacosub_decode_frame,
     .flush          = ff_ass_decoder_flush,
     .priv_data_size = sizeof(FFASSDecoderContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c
index 1b565b2fc9..ab7e0e02d5 100644
--- a/libavcodec/libaribb24.c
+++ b/libavcodec/libaribb24.c
@@ -283,10 +283,10 @@ next_region:
     return ret;
 }
 
-static int libaribb24_decode(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *pkt)
+static int libaribb24_decode(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *pkt)
 {
     Libaribb24Context *b24 = avctx->priv_data;
-    AVSubtitle *sub = data;
     size_t parsed_data_size = 0;
     size_t decoded_subtitle_size = 0;
     const unsigned char *parsed_data = NULL;
@@ -391,6 +391,6 @@ const FFCodec ff_libaribb24_decoder = {
     .priv_data_size = sizeof(Libaribb24Context),
     .init      = libaribb24_init,
     .close     = libaribb24_close,
-    .decode    = libaribb24_decode,
+    .decode_sub = libaribb24_decode,
     .flush     = libaribb24_flush,
 };
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 5ebf47f796..4920c61e0f 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -637,10 +637,10 @@ static int slice_to_vbi_lines(TeletextContext *ctx, uint8_t* buf, int size)
     return lines;
 }
 
-static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *pkt)
+static int teletext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *pkt)
 {
     TeletextContext *ctx = avctx->priv_data;
-    AVSubtitle      *sub = data;
     int             ret = 0;
 
     if (!ctx->vbi) {
@@ -822,6 +822,6 @@ const FFCodec ff_libzvbi_teletext_decoder = {
     .priv_data_size = sizeof(TeletextContext),
     .init      = teletext_init_decoder,
     .close     = teletext_close_decoder,
-    .decode    = teletext_decode_frame,
+    .decode_sub = teletext_decode_frame,
     .flush     = teletext_flush,
 };
diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c
index 0863fe1494..1cd5d1d5f5 100644
--- a/libavcodec/microdvddec.c
+++ b/libavcodec/microdvddec.c
@@ -274,10 +274,9 @@ static void microdvd_close_no_persistent_tags(AVBPrint *new_line,
     }
 }
 
-static int microdvd_decode_frame(AVCodecContext *avctx,
-                                 void *data, int *got_sub_ptr, AVPacket *avpkt)
+static int microdvd_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt)
 {
-    AVSubtitle *sub = data;
     AVBPrint new_line;
     char *line = avpkt->data;
     char *end = avpkt->data + avpkt->size;
@@ -375,7 +374,7 @@ const FFCodec ff_microdvd_decoder = {
     .p.type       = AVMEDIA_TYPE_SUBTITLE,
     .p.id         = AV_CODEC_ID_MICRODVD,
     .init         = microdvd_init,
-    .decode       = microdvd_decode_frame,
+    .decode_sub   = microdvd_decode_frame,
     .flush        = ff_ass_decoder_flush,
     .priv_data_size = sizeof(FFASSDecoderContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c
index 3ba7043180..c3e0a75e57 100644
--- a/libavcodec/movtextdec.c
+++ b/libavcodec/movtextdec.c
@@ -472,10 +472,9 @@ static int mov_text_init(AVCodecContext *avctx) {
         return ff_ass_subtitle_header_default(avctx);
 }
 
-static int mov_text_decode_frame(AVCodecContext *avctx,
-                            void *data, int *got_sub_ptr, AVPacket *avpkt)
+static int mov_text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt)
 {
-    AVSubtitle *sub = data;
     MovTextContext *m = avctx->priv_data;
     int ret;
     AVBPrint buf;
@@ -600,7 +599,7 @@ const FFCodec ff_movtext_decoder = {
     .priv_data_size = sizeof(MovTextContext),
     .p.priv_class = &mov_text_decoder_class,
     .init         = mov_text_init,
-    .decode       = mov_text_decode_frame,
+    .decode_sub   = mov_text_decode_frame,
     .close        = mov_text_decode_close,
     .flush        = mov_text_flush,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c
index bfcd72bf2d..490a3e6d45 100644
--- a/libavcodec/mpl2dec.c
+++ b/libavcodec/mpl2dec.c
@@ -63,12 +63,11 @@ static int mpl2_event_to_ass(AVBPrint *buf, const char *p)
     return 0;
 }
 
-static int mpl2_decode_frame(AVCodecContext *avctx, void *data,
+static int mpl2_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
                              int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
     AVBPrint buf;
-    AVSubtitle *sub = data;
     const char *ptr = avpkt->data;
     FFASSDecoderContext *s = avctx->priv_data;
 
@@ -87,7 +86,7 @@ const FFCodec ff_mpl2_decoder = {
     .p.long_name    = NULL_IF_CONFIG_SMALL("MPL2 subtitle"),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_MPL2,
-    .decode         = mpl2_decode_frame,
+    .decode_sub     = mpl2_decode_frame,
     .init           = ff_ass_subtitle_header_default,
     .flush          = ff_ass_decoder_flush,
     .priv_data_size = sizeof(FFASSDecoderContext),
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index 4cd5e237f2..8b5eb5f816 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -494,10 +494,9 @@ static int parse_presentation_segment(AVCodecContext *avctx,
  * @param buf pointer to the packet to process
  * @param buf_size size of packet to process
  */
-static int display_end_segment(AVCodecContext *avctx, void *data,
+static int display_end_segment(AVCodecContext *avctx, AVSubtitle *sub,
                                const uint8_t *buf, int buf_size)
 {
-    AVSubtitle    *sub = data;
     PGSSubContext *ctx = avctx->priv_data;
     int64_t pts;
     PGSSubPalette *palette;
@@ -590,8 +589,8 @@ static int display_end_segment(AVCodecContext *avctx, void *data,
     return 1;
 }
 
-static int decode(AVCodecContext *avctx, void *data, int *got_sub_ptr,
-                  AVPacket *avpkt)
+static int decode(AVCodecContext *avctx, AVSubtitle *sub,
+                  int *got_sub_ptr, AVPacket *avpkt)
 {
     const uint8_t *buf = avpkt->data;
     int buf_size       = avpkt->size;
@@ -639,7 +638,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub_ptr,
             ret = parse_object_segment(avctx, buf, segment_length);
             break;
         case PRESENTATION_SEGMENT:
-            ret = parse_presentation_segment(avctx, buf, segment_length, ((AVSubtitle*)(data))->pts);
+            ret = parse_presentation_segment(avctx, buf, segment_length, sub->pts);
             break;
         case WINDOW_SEGMENT:
             /*
@@ -657,7 +656,7 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub_ptr,
                 ret = AVERROR_INVALIDDATA;
                 break;
             }
-            ret = display_end_segment(avctx, data, buf, segment_length);
+            ret = display_end_segment(avctx, sub, buf, segment_length);
             if (ret >= 0)
                 *got_sub_ptr = ret;
             break;
@@ -699,7 +698,7 @@ const FFCodec ff_pgssub_decoder = {
     .priv_data_size = sizeof(PGSSubContext),
     .init           = init_decoder,
     .close          = close_decoder,
-    .decode         = decode,
+    .decode_sub     = decode,
     .p.priv_class   = &pgsdec_class,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c
index 4c0694c8ed..1612554461 100644
--- a/libavcodec/realtextdec.c
+++ b/libavcodec/realtextdec.c
@@ -56,11 +56,10 @@ static int rt_event_to_ass(AVBPrint *buf, const char *p)
     return 0;
 }
 
-static int realtext_decode_frame(AVCodecContext *avctx,
-                                 void *data, int *got_sub_ptr, AVPacket *avpkt)
+static int realtext_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                                 int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
-    AVSubtitle *sub = data;
     const char *ptr = avpkt->data;
     FFASSDecoderContext *s = avctx->priv_data;
     AVBPrint buf;
@@ -80,7 +79,7 @@ const FFCodec ff_realtext_decoder = {
     .p.long_name    = NULL_IF_CONFIG_SMALL("RealText subtitle"),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_REALTEXT,
-    .decode         = realtext_decode_frame,
+    .decode_sub     = realtext_decode_frame,
     .init           = ff_ass_subtitle_header_default,
     .flush          = ff_ass_decoder_flush,
     .priv_data_size = sizeof(FFASSDecoderContext),
diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c
index 2c8c31e4f6..f2cf5783af 100644
--- a/libavcodec/samidec.c
+++ b/libavcodec/samidec.c
@@ -132,10 +132,9 @@ end:
     return ret;
 }
 
-static int sami_decode_frame(AVCodecContext *avctx,
-                             void *data, int *got_sub_ptr, AVPacket *avpkt)
+static int sami_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                             int *got_sub_ptr, AVPacket *avpkt)
 {
-    AVSubtitle *sub = data;
     const char *ptr = avpkt->data;
     SAMIContext *sami = avctx->priv_data;
 
@@ -189,7 +188,7 @@ const FFCodec ff_sami_decoder = {
     .priv_data_size = sizeof(SAMIContext),
     .init           = sami_init,
     .close          = sami_close,
-    .decode         = sami_decode_frame,
+    .decode_sub     = sami_decode_frame,
     .flush          = sami_flush,
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
 };
diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c
index 5de360b821..aa975d16c7 100644
--- a/libavcodec/srtdec.c
+++ b/libavcodec/srtdec.c
@@ -55,10 +55,9 @@ static int srt_to_ass(AVCodecContext *avctx, AVBPrint *dst,
     return ff_htmlmarkup_to_ass(avctx, dst, in);
 }
 
-static int srt_decode_frame(AVCodecContext *avctx,
-                            void *data, int *got_sub_ptr, AVPacket *avpkt)
+static int srt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                            int *got_sub_ptr, AVPacket *avpkt)
 {
-    AVSubtitle *sub = data;
     AVBPrint buffer;
     int x1 = -1, y1 = -1, x2 = -1, y2 = -1;
     int ret;
@@ -97,7 +96,7 @@ const FFCodec ff_srt_decoder = {
     .p.type       = AVMEDIA_TYPE_SUBTITLE,
     .p.id         = AV_CODEC_ID_SUBRIP,
     .init         = ff_ass_subtitle_header_default,
-    .decode       = srt_decode_frame,
+    .decode_sub   = srt_decode_frame,
     .flush        = ff_ass_decoder_flush,
     .priv_data_size = sizeof(FFASSDecoderContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
@@ -111,7 +110,7 @@ const FFCodec ff_subrip_decoder = {
     .p.type       = AVMEDIA_TYPE_SUBTITLE,
     .p.id         = AV_CODEC_ID_SUBRIP,
     .init         = ff_ass_subtitle_header_default,
-    .decode       = srt_decode_frame,
+    .decode_sub   = srt_decode_frame,
     .flush        = ff_ass_decoder_flush,
     .priv_data_size = sizeof(FFASSDecoderContext),
     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c
index cbc3056c90..9001dd3d45 100644
--- a/libavcodec/subviewerdec.c
+++ b/libavcodec/subviewerdec.c
@@ -47,11 +47,10 @@ static int subviewer_event_to_ass(AVBPrint *buf, const char *p)
     return 0;
 }
 
-static int subviewer_decode_frame(AVCodecContext *avctx,
-                                  void *data, int *got_sub_ptr, AVPacket *avpkt)
+static int subviewer_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                                  int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
-    AVSubtitle *sub = data;
     const char *ptr = avpkt->data;
     FFASSDecoderContext *s = avctx->priv_data;
     AVBPrint buf;
@@ -71,7 +70,7 @@ const FFCodec ff_subviewer_decoder = {
     .p.long_name    = NULL_IF_CONFIG_SMALL("SubViewer subtitle"),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_SUBVIEWER,
-    .decode         = subviewer_decode_frame,
+    .decode_sub     = subviewer_decode_frame,
     .init           = ff_ass_subtitle_header_default,
     .flush          = ff_ass_decoder_flush,
     .priv_data_size = sizeof(FFASSDecoderContext),
diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c
index 075ad632c4..f77d7aebe9 100644
--- a/libavcodec/tests/avcodec.c
+++ b/libavcodec/tests/avcodec.c
@@ -135,12 +135,13 @@ int main(void){
                 codec->capabilities & AV_CODEC_CAP_ENCODER_FLUSH)
                 ERR("Frame-threaded encoder %s claims to support flushing\n");
         } else {
-            if (codec->type == AVMEDIA_TYPE_SUBTITLE && !codec2->decode)
-                ERR("Subtitle decoder %s does not implement decode callback\n");
+            if (codec->type == AVMEDIA_TYPE_SUBTITLE && !codec2->decode_sub)
+                ERR("Subtitle decoder %s does not implement decode_sub callback\n");
             if (codec->type == AVMEDIA_TYPE_SUBTITLE && codec2->bsfs)
                 ERR("Automatic bitstream filtering unsupported for subtitles; "
                     "yet decoder %s has it set\n");
-            if (!!codec2->decode + !!codec2->receive_frame != 1)
+            if (codec->type != AVMEDIA_TYPE_SUBTITLE !=
+                !!codec2->decode + !!codec2->receive_frame)
                 ERR("Decoder %s does not implement exactly one decode API.\n");
             if (codec->capabilities & (AV_CODEC_CAP_SMALL_LAST_FRAME    |
                                        AV_CODEC_CAP_VARIABLE_FRAME_SIZE |
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
index 95450a18f7..48e7e14602 100644
--- a/libavcodec/textdec.c
+++ b/libavcodec/textdec.c
@@ -45,12 +45,11 @@ static const AVOption options[] = {
     { NULL }
 };
 
-static int text_decode_frame(AVCodecContext *avctx, void *data,
+static int text_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
                              int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
     AVBPrint buf;
-    AVSubtitle *sub = data;
     const char *ptr = avpkt->data;
     TextContext *text = avctx->priv_data;
 
@@ -87,7 +86,7 @@ const FFCodec ff_text_decoder = {
     .priv_data_size = sizeof(TextContext),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_TEXT,
-    .decode         = text_decode_frame,
+    .decode_sub     = text_decode_frame,
     .init           = ff_ass_subtitle_header_default,
     .p.priv_class   = &textsub_decoder_class,
     .flush          = text_flush,
@@ -111,7 +110,7 @@ const FFCodec ff_vplayer_decoder = {
     .priv_data_size = sizeof(TextContext),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_VPLAYER,
-    .decode         = text_decode_frame,
+    .decode_sub     = text_decode_frame,
     .init           = linebreak_init,
     .p.priv_class   = &textsub_decoder_class,
     .flush          = text_flush,
@@ -126,7 +125,7 @@ const FFCodec ff_stl_decoder = {
     .priv_data_size = sizeof(TextContext),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_STL,
-    .decode         = text_decode_frame,
+    .decode_sub     = text_decode_frame,
     .init           = linebreak_init,
     .p.priv_class   = &textsub_decoder_class,
     .flush          = text_flush,
@@ -141,7 +140,7 @@ const FFCodec ff_pjs_decoder = {
     .priv_data_size = sizeof(TextContext),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_PJS,
-    .decode         = text_decode_frame,
+    .decode_sub     = text_decode_frame,
     .init           = linebreak_init,
     .p.priv_class   = &textsub_decoder_class,
     .flush          = text_flush,
@@ -156,7 +155,7 @@ const FFCodec ff_subviewer1_decoder = {
     .priv_data_size = sizeof(TextContext),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_SUBVIEWER1,
-    .decode         = text_decode_frame,
+    .decode_sub     = text_decode_frame,
     .init           = linebreak_init,
     .p.priv_class   = &textsub_decoder_class,
     .flush          = text_flush,
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 1666f5dabb..34d97022b7 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -80,7 +80,7 @@ int av_codec_is_encoder(const AVCodec *avcodec)
 int av_codec_is_decoder(const AVCodec *avcodec)
 {
     const FFCodec *const codec = ffcodec(avcodec);
-    return codec && (codec->decode || codec->receive_frame);
+    return codec && (codec->decode || codec->decode_sub || codec->receive_frame);
 }
 
 int ff_set_dimensions(AVCodecContext *s, int width, int height)
diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c
index 4ebbc63074..8cb7fea070 100644
--- a/libavcodec/webvttdec.c
+++ b/libavcodec/webvttdec.c
@@ -80,11 +80,10 @@ static int webvtt_event_to_ass(AVBPrint *buf, const char *p)
     return 0;
 }
 
-static int webvtt_decode_frame(AVCodecContext *avctx,
-                               void *data, int *got_sub_ptr, AVPacket *avpkt)
+static int webvtt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                               int *got_sub_ptr, AVPacket *avpkt)
 {
     int ret = 0;
-    AVSubtitle *sub = data;
     const char *ptr = avpkt->data;
     FFASSDecoderContext *s = avctx->priv_data;
     AVBPrint buf;
@@ -104,7 +103,7 @@ const FFCodec ff_webvtt_decoder = {
     .p.long_name    = NULL_IF_CONFIG_SMALL("WebVTT subtitle"),
     .p.type         = AVMEDIA_TYPE_SUBTITLE,
     .p.id           = AV_CODEC_ID_WEBVTT,
-    .decode         = webvtt_decode_frame,
+    .decode_sub     = webvtt_decode_frame,
     .init           = ff_ass_subtitle_header_default,
     .flush          = ff_ass_decoder_flush,
     .priv_data_size = sizeof(FFASSDecoderContext),
diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c
index cb7b7b7cab..c2c1b920cc 100644
--- a/libavcodec/xsubdec.c
+++ b/libavcodec/xsubdec.c
@@ -47,11 +47,11 @@ static int64_t parse_timecode(const uint8_t *buf, int64_t packet_time) {
     return ms - packet_time;
 }
 
-static int decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr,
-                        AVPacket *avpkt) {
+static int decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
+                        int *got_sub_ptr, AVPacket *avpkt)
+{
     const uint8_t *buf = avpkt->data;
     int buf_size = avpkt->size;
-    AVSubtitle *sub = data;
     AVSubtitleRect *rect;
     const uint8_t *buf_end = buf + buf_size;
     uint8_t *bitmap;
@@ -160,6 +160,6 @@ const FFCodec ff_xsub_decoder = {
     .p.type    = AVMEDIA_TYPE_SUBTITLE,
     .p.id      = AV_CODEC_ID_XSUB,
     .init      = decode_init,
-    .decode    = decode_frame,
+    .decode_sub = decode_frame,
     .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
 };
