aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClément Bœsch <u@pkh.me>2013-09-29 22:11:51 +0200
committerClément Bœsch <u@pkh.me>2013-10-04 07:59:48 +0200
commit95cafeb684d70002ee05fd6ed0d8a1188ed84828 (patch)
tree9ca5060cf9a0803220a50c647ce81c2c3736c21f
parent71e0ae22aca7dbe13a127ed305b3ef987c9abc58 (diff)
downloadffmpeg-95cafeb684d70002ee05fd6ed0d8a1188ed84828.tar.gz
avcodec/dvdsubdec: reconstruct incomplete SPU packets.
-rw-r--r--libavcodec/dvdsubdec.c49
-rw-r--r--tests/ref/fate/sub2video1
2 files changed, 50 insertions, 0 deletions
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