diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c index 9caf176dba..b2445ebc1b 100644 --- a/libavcodec/dvdsubdec.c +++ b/libavcodec/dvdsubdec.c @@ -35,6 +35,8 @@ typedef struct DVDSubContext int has_palette; uint8_t colormap[4]; uint8_t alpha[256]; + uint8_t *buf; + int buf_size; #ifdef DEBUG int sub_id; #endif @@ -230,6 +232,9 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header, cmd_pos = READ_OFFSET(buf + cmd_pos); + if (cmd_pos < 0 || cmd_pos > buf_size - 2 - offset_size) + return AVERROR(EAGAIN); + while (cmd_pos > 0 && cmd_pos < buf_size - 2 - offset_size) { date = AV_RB16(buf + cmd_pos); next_cmd_pos = READ_OFFSET(buf + cmd_pos + 2); @@ -485,6 +490,25 @@ static void ppm_save(const char *filename, uint8_t *bitmap, int w, int h, } #endif +static int append_to_cached_buf(AVCodecContext *avctx, + const uint8_t *buf, int buf_size) +{ + DVDSubContext *ctx = avctx->priv_data; + + if (ctx->buf_size > 0xffff - buf_size) { + av_log(avctx, AV_LOG_WARNING, "Attempt to reconstruct " + "too large SPU packets aborted.\n"); + av_freep(&ctx->buf); + return AVERROR_INVALIDDATA; + } + ctx->buf = av_realloc(ctx->buf, ctx->buf_size + buf_size); + if (!ctx->buf) + return AVERROR(ENOMEM); + memcpy(ctx->buf + ctx->buf_size, buf, buf_size); + ctx->buf_size += buf_size; + return 0; +} + static int dvdsub_decode(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt) @@ -495,7 +519,21 @@ static int dvdsub_decode(AVCodecContext *avctx, AVSubtitle *sub = data; int is_menu; + if (ctx->buf) { + int ret = append_to_cached_buf(avctx, buf, buf_size); + if (ret < 0) { + *data_size = 0; + return ret; + } + buf = ctx->buf; + buf_size = ctx->buf_size; + } + is_menu = decode_dvd_subtitles(ctx, sub, buf, buf_size); + if (is_menu == AVERROR(EAGAIN)) { + *data_size = 0; + return append_to_cached_buf(avctx, buf, buf_size); + } if (is_menu < 0) { no_subtitle: @@ -519,6 +557,8 @@ static int dvdsub_decode(AVCodecContext *avctx, } #endif + av_freep(&ctx->buf); + ctx->buf_size = 0; *data_size = 1; return buf_size; } @@ -592,6 +632,14 @@ static av_cold int dvdsub_init(AVCodecContext *avctx) return 1; } +static av_cold int dvdsub_close(AVCodecContext *avctx) +{ + DVDSubContext *ctx = avctx->priv_data; + av_freep(&ctx->buf); + ctx->buf_size = 0; + return 0; +} + #define OFFSET(field) offsetof(DVDSubContext, field) #define VD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { @@ -612,6 +660,7 @@ AVCodec ff_dvdsub_decoder = { .priv_data_size = sizeof(DVDSubContext), .init = dvdsub_init, .decode = dvdsub_decode, + .close = dvdsub_close, .long_name = NULL_IF_CONFIG_SMALL("DVD subtitles"), .priv_class = &dvdsub_class, }; diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video index f866c21105..d052c100b5 100644 --- a/tests/ref/fate/sub2video +++ b/tests/ref/fate/sub2video @@ -77,6 +77,7 @@ 1, 141556, 141556, 1661, 1088, 0xde20aa20, F=0x0 1, 163445, 163445, 1331, 339, 0x8bd186ef, F=0x0 1, 168049, 168049, 1900, 1312, 0x0bf20e8d, F=0x0 +1, 170035, 170035, 1524, 1279, 0xb6c2dafe, F=0x0 1, 172203, 172203, 1695, 1826, 0x9a1ac769, F=0x0 1, 173947, 173947, 1934, 1474, 0xa9b03cdc, F=0x0 1, 175957, 175957, 1763, 1019, 0x20409355, F=0x0