avcodec/dvdsubdec: reconstruct incomplete SPU packets.
authorClément Bœsch <u@pkh.me>
Sun, 29 Sep 2013 20:11:51 +0000 (22:11 +0200)
committerClément Bœsch <u@pkh.me>
Fri, 4 Oct 2013 05:59:48 +0000 (07:59 +0200)
libavcodec/dvdsubdec.c
tests/ref/fate/sub2video

index 9caf176..b2445eb 100644 (file)
@@ -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,
 };
index f866c21..d052c10 100644 (file)
@@ -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