Merge commit 'b8cd7a3c8df2c3aac8d7a0b5a02d83caf61bd769'
authorClément Bœsch <u@pkh.me>
Mon, 3 Apr 2017 18:57:03 +0000 (20:57 +0200)
committerClément Bœsch <u@pkh.me>
Mon, 3 Apr 2017 19:01:40 +0000 (21:01 +0200)
* commit 'b8cd7a3c8df2c3aac8d7a0b5a02d83caf61bd769':
  dvbsub: Check for errors from system()

printf(...) replaced with fprintf(stderr, ...) for logging errors.

Merged-by: Clément Bœsch <u@pkh.me>
1  2 
libavcodec/dvbsubdec.c

diff --combined libavcodec/dvbsubdec.c
@@@ -2,20 -2,20 +2,20 @@@
   * DVB subtitle decoding
   * Copyright (c) 2005 Ian Caulfield
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
@@@ -24,7 -24,6 +24,7 @@@
  #include "bytestream.h"
  #include "internal.h"
  #include "libavutil/colorspace.h"
 +#include "libavutil/opt.h"
  
  #define DVBSUB_PAGE_SEGMENT     0x10
  #define DVBSUB_REGION_SEGMENT   0x11
@@@ -85,10 -84,16 +85,16 @@@ static void png_save(const char *filena
      fclose(f);
  
      snprintf(command, sizeof(command), "pnmtopng -alpha %s %s > %s.png 2> /dev/null", fname2, fname, filename);
-     system(command);
+     if (system(command) != 0) {
 -        printf("Error running pnmtopng\n");
++        fprintf(stderr, "Error running pnmtopng\n");
+         return;
+     }
  
      snprintf(command, sizeof(command), "rm %s %s", fname, fname2);
-     system(command);
+     if (system(command) != 0) {
 -        printf("Error removing %s and %s\n", fname, fname2);
++        fprintf(stderr, "Error removing %s and %s\n", fname, fname2);
+         return;
+     }
  }
  #endif
  
  
  typedef struct DVBSubCLUT {
      int id;
 +    int version;
  
      uint32_t clut4[4];
      uint32_t clut16[16];
@@@ -123,7 -127,6 +129,7 @@@ typedef struct DVBSubObjectDisplay 
  
  typedef struct DVBSubObject {
      int id;
 +    int version;
  
      int type;
  
@@@ -143,7 -146,6 +149,7 @@@ typedef struct DVBSubRegionDisplay 
  
  typedef struct DVBSubRegion {
      int id;
 +    int version;
  
      int width;
      int height;
  
      uint8_t *pbuf;
      int buf_size;
 +    int dirty;
  
      DVBSubObjectDisplay *display_list;
  
@@@ -171,21 -172,15 +177,21 @@@ typedef struct DVBSubDisplayDefinition 
  } DVBSubDisplayDefinition;
  
  typedef struct DVBSubContext {
 +    AVClass *class;
      int composition_id;
      int ancillary_id;
  
 +    int version;
      int time_out;
 +    int compute_edt; /**< if 1 end display time calculated using pts
 +                          if 0 (Default) calculated using time out */
 +    int compute_clut;
 +    int substream;
 +    int64_t prev_start;
      DVBSubRegion *region_list;
      DVBSubCLUT   *clut_list;
      DVBSubObject *object_list;
  
 -    int display_list_size;
      DVBSubRegionDisplay *display_list;
      DVBSubDisplayDefinition *display_definition;
  } DVBSubContext;
@@@ -251,59 -246,53 +257,59 @@@ static void delete_region_display_list(
                      obj2 = *obj2_ptr;
  
                      while (obj2 != object) {
 -                        assert(obj2);
 +                        av_assert0(obj2);
                          obj2_ptr = &obj2->next;
                          obj2 = *obj2_ptr;
                      }
  
                      *obj2_ptr = obj2->next;
  
 -                    av_free(obj2);
 +                    av_freep(&obj2);
                  }
              }
          }
  
          region->display_list = display->region_list_next;
  
 -        av_free(display);
 +        av_freep(&display);
      }
  
  }
  
 -static void delete_state(DVBSubContext *ctx)
 +static void delete_cluts(DVBSubContext *ctx)
  {
 -    DVBSubRegion *region;
 -    DVBSubCLUT *clut;
 -
 -    while (ctx->region_list) {
 -        region = ctx->region_list;
 +    while (ctx->clut_list) {
 +        DVBSubCLUT *clut = ctx->clut_list;
  
 -        ctx->region_list = region->next;
 +        ctx->clut_list = clut->next;
  
 -        delete_region_display_list(ctx, region);
 -        av_free(region->pbuf);
 -        av_free(region);
 +        av_freep(&clut);
      }
 +}
  
 -    while (ctx->clut_list) {
 -        clut = ctx->clut_list;
 +static void delete_objects(DVBSubContext *ctx)
 +{
 +    while (ctx->object_list) {
 +        DVBSubObject *object = ctx->object_list;
  
 -        ctx->clut_list = clut->next;
 +        ctx->object_list = object->next;
  
 -        av_free(clut);
 +        av_freep(&object);
      }
 +}
  
 -    av_freep(&ctx->display_definition);
 +static void delete_regions(DVBSubContext *ctx)
 +{
 +    while (ctx->region_list) {
 +        DVBSubRegion *region = ctx->region_list;
 +
 +        ctx->region_list = region->next;
  
 -    /* Should already be null */
 -    if (ctx->object_list)
 -        av_log(NULL, AV_LOG_ERROR, "Memory deallocation error!\n");
 +        delete_region_display_list(ctx, region);
 +
 +        av_freep(&region->pbuf);
 +        av_freep(&region);
 +    }
  }
  
  static av_cold int dvbsub_init_decoder(AVCodecContext *avctx)
      int i, r, g, b, a = 0;
      DVBSubContext *ctx = avctx->priv_data;
  
 -    if (!avctx->extradata || avctx->extradata_size != 4) {
 -        av_log(avctx, AV_LOG_WARNING, "Invalid extradata, subtitle streams may be combined!\n");
 +    if (ctx->substream < 0) {
 +        ctx->composition_id = -1;
 +        ctx->ancillary_id   = -1;
 +    } else if (!avctx->extradata || (avctx->extradata_size < 4) || ((avctx->extradata_size % 5 != 0) && (avctx->extradata_size != 4))) {
 +        av_log(avctx, AV_LOG_WARNING, "Invalid DVB subtitles stream extradata!\n");
          ctx->composition_id = -1;
          ctx->ancillary_id   = -1;
      } else {
 -        ctx->composition_id = AV_RB16(avctx->extradata);
 -        ctx->ancillary_id   = AV_RB16(avctx->extradata + 2);
 +        if (avctx->extradata_size > 5*ctx->substream + 2) {
 +            ctx->composition_id = AV_RB16(avctx->extradata + 5*ctx->substream);
 +            ctx->ancillary_id   = AV_RB16(avctx->extradata + 5*ctx->substream + 2);
 +        } else {
 +            av_log(avctx, AV_LOG_WARNING, "Selected DVB subtitles sub-stream %d is not available\n", ctx->substream);
 +            ctx->composition_id = AV_RB16(avctx->extradata);
 +            ctx->ancillary_id   = AV_RB16(avctx->extradata + 2);
 +        }
      }
  
 +    ctx->version = -1;
 +    ctx->prev_start = AV_NOPTS_VALUE;
 +
      default_clut.id = -1;
      default_clut.next = NULL;
  
@@@ -400,39 -377,30 +406,39 @@@ static av_cold int dvbsub_close_decoder
      DVBSubContext *ctx = avctx->priv_data;
      DVBSubRegionDisplay *display;
  
 -    delete_state(ctx);
 +    delete_regions(ctx);
 +
 +    delete_objects(ctx);
 +
 +    delete_cluts(ctx);
 +
 +    av_freep(&ctx->display_definition);
  
      while (ctx->display_list) {
          display = ctx->display_list;
          ctx->display_list = display->next;
  
 -        av_free(display);
 +        av_freep(&display);
      }
  
      return 0;
  }
  
 -static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len,
 +static int dvbsub_read_2bit_string(AVCodecContext *avctx,
 +                                   uint8_t *destbuf, int dbuf_len,
                                     const uint8_t **srcbuf, int buf_size,
 -                                   int non_mod, uint8_t *map_table)
 +                                   int non_mod, uint8_t *map_table, int x_pos)
  {
      GetBitContext gb;
  
      int bits;
      int run_length;
 -    int pixels_read = 0;
 +    int pixels_read = x_pos;
  
      init_get_bits(&gb, *srcbuf, buf_size << 3);
  
 +    destbuf += x_pos;
 +
      while (get_bits_count(&gb) < buf_size << 3 && pixels_read < dbuf_len) {
          bits = get_bits(&gb, 2);
  
                              }
                          }
                      } else if (bits == 1) {
 -                        pixels_read += 2;
                          if (map_table)
                              bits = map_table[0];
                          else
                              bits = 0;
 -                        if (pixels_read <= dbuf_len) {
 -                            *destbuf++ = bits;
 +                        run_length = 2;
 +                        while (run_length-- > 0 && pixels_read < dbuf_len) {
                              *destbuf++ = bits;
 +                            pixels_read++;
                          }
                      } else {
                          (*srcbuf) += (get_bits_count(&gb) + 7) >> 3;
      }
  
      if (get_bits(&gb, 6))
 -        av_log(NULL, AV_LOG_ERROR, "DVBSub error: line overflow\n");
 +        av_log(avctx, AV_LOG_ERROR, "line overflow\n");
  
      (*srcbuf) += (get_bits_count(&gb) + 7) >> 3;
  
      return pixels_read;
  }
  
 -static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len,
 +static int dvbsub_read_4bit_string(AVCodecContext *avctx, uint8_t *destbuf, int dbuf_len,
                                     const uint8_t **srcbuf, int buf_size,
 -                                   int non_mod, uint8_t *map_table)
 +                                   int non_mod, uint8_t *map_table, int x_pos)
  {
      GetBitContext gb;
  
      int bits;
      int run_length;
 -    int pixels_read = 0;
 +    int pixels_read = x_pos;
  
      init_get_bits(&gb, *srcbuf, buf_size << 3);
  
 +    destbuf += x_pos;
 +
      while (get_bits_count(&gb) < buf_size << 3 && pixels_read < dbuf_len) {
          bits = get_bits(&gb, 4);
  
                              }
                          }
                      } else if (bits == 1) {
 -                        pixels_read += 2;
                          if (map_table)
                              bits = map_table[0];
                          else
                              bits = 0;
 -                        if (pixels_read <= dbuf_len) {
 -                            *destbuf++ = bits;
 +                        run_length = 2;
 +                        while (run_length-- > 0 && pixels_read < dbuf_len) {
                              *destbuf++ = bits;
 +                            pixels_read++;
                          }
                      } else {
                          if (map_table)
      }
  
      if (get_bits(&gb, 8))
 -        av_log(NULL, AV_LOG_ERROR, "DVBSub error: line overflow\n");
 +        av_log(avctx, AV_LOG_ERROR, "line overflow\n");
  
      (*srcbuf) += (get_bits_count(&gb) + 7) >> 3;
  
      return pixels_read;
  }
  
 -static int dvbsub_read_8bit_string(uint8_t *destbuf, int dbuf_len,
 +static int dvbsub_read_8bit_string(AVCodecContext *avctx,
 +                                   uint8_t *destbuf, int dbuf_len,
                                      const uint8_t **srcbuf, int buf_size,
 -                                    int non_mod, uint8_t *map_table)
 +                                    int non_mod, uint8_t *map_table, int x_pos)
  {
      const uint8_t *sbuf_end = (*srcbuf) + buf_size;
      int bits;
      int run_length;
 -    int pixels_read = 0;
 +    int pixels_read = x_pos;
 +
 +    destbuf += x_pos;
  
      while (*srcbuf < sbuf_end && pixels_read < dbuf_len) {
          bits = *(*srcbuf)++;
                  if (run_length == 0) {
                      return pixels_read;
                  }
 +
 +                bits = 0;
              } else {
                  bits = *(*srcbuf)++;
 -
 -                if (non_mod == 1 && bits == 1)
 -                    pixels_read += run_length;
              }
 -            if (map_table)
 -                bits = map_table[0];
 -            else
 -                bits = 0;
 -            while (run_length-- > 0 && pixels_read < dbuf_len) {
 -                *destbuf++ = bits;
 -                pixels_read++;
 +            if (non_mod == 1 && bits == 1)
 +                pixels_read += run_length;
 +            else {
 +                if (map_table)
 +                    bits = map_table[bits];
 +                while (run_length-- > 0 && pixels_read < dbuf_len) {
 +                    *destbuf++ = bits;
 +                    pixels_read++;
 +                }
              }
          }
      }
  
      if (*(*srcbuf)++)
 -        av_log(NULL, AV_LOG_ERROR, "DVBSub error: line overflow\n");
 +        av_log(avctx, AV_LOG_ERROR, "line overflow\n");
  
      return pixels_read;
  }
  
 +static void compute_default_clut(AVSubtitleRect *rect, int w, int h)
 +{
 +    uint8_t list[256] = {0};
 +    uint8_t list_inv[256];
 +    int counttab[256] = {0};
 +    int count, i, x, y;
 +
 +#define V(x,y) rect->data[0][(x) + (y)*rect->linesize[0]]
 +    for (y = 0; y<h; y++) {
 +        for (x = 0; x<w; x++) {
 +            int v = V(x,y) + 1;
 +            int vl = x     ? V(x-1,y) + 1 : 0;
 +            int vr = x+1<w ? V(x+1,y) + 1 : 0;
 +            int vt = y     ? V(x,y-1) + 1 : 0;
 +            int vb = y+1<h ? V(x,y+1) + 1 : 0;
 +            counttab[v-1] += !!((v!=vl) + (v!=vr) + (v!=vt) + (v!=vb));
 +        }
 +    }
 +#define L(x,y) list[ rect->data[0][(x) + (y)*rect->linesize[0]] ]
 +
 +    for (i = 0; i<256; i++) {
 +        int scoretab[256] = {0};
 +        int bestscore = 0;
 +        int bestv = 0;
 +        for (y = 0; y<h; y++) {
 +            for (x = 0; x<w; x++) {
 +                int v = rect->data[0][x + y*rect->linesize[0]];
 +                int l_m = list[v];
 +                int l_l = x     ? L(x-1, y) : 1;
 +                int l_r = x+1<w ? L(x+1, y) : 1;
 +                int l_t = y     ? L(x, y-1) : 1;
 +                int l_b = y+1<h ? L(x, y+1) : 1;
 +                int score;
 +                if (l_m)
 +                    continue;
 +                scoretab[v] += l_l + l_r + l_t + l_b;
 +                score = 1024LL*scoretab[v] / counttab[v];
 +                if (score > bestscore) {
 +                    bestscore = score;
 +                    bestv = v;
 +                }
 +            }
 +        }
 +        if (!bestscore)
 +            break;
 +        list    [ bestv ] = 1;
 +        list_inv[     i ] = bestv;
 +    }
 +
 +    count = FFMAX(i - 1, 1);
 +    for (i--; i>=0; i--) {
 +        int v = i*255/count;
 +        AV_WN32(rect->data[1] + 4*list_inv[i], RGBA(v/2,v,v/2,v));
 +    }
 +}
 +
 +
 +static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_output)
 +{
 +    DVBSubContext *ctx = avctx->priv_data;
 +    DVBSubRegionDisplay *display;
 +    DVBSubDisplayDefinition *display_def = ctx->display_definition;
 +    DVBSubRegion *region;
 +    AVSubtitleRect *rect;
 +    DVBSubCLUT *clut;
 +    uint32_t *clut_table;
 +    int i;
 +    int offset_x=0, offset_y=0;
 +    int ret = 0;
 +
 +
 +    if (display_def) {
 +        offset_x = display_def->x;
 +        offset_y = display_def->y;
 +    }
 +
 +    /* Not touching AVSubtitles again*/
 +    if(sub->num_rects) {
 +        avpriv_request_sample(ctx, "Different Version of Segment asked Twice");
 +        return AVERROR_PATCHWELCOME;
 +    }
 +    for (display = ctx->display_list; display; display = display->next) {
 +        region = get_region(ctx, display->region_id);
 +        if (region && region->dirty)
 +            sub->num_rects++;
 +    }
 +
 +    if(ctx->compute_edt == 0) {
 +        sub->end_display_time = ctx->time_out * 1000;
 +        *got_output = 1;
 +    } else if (ctx->prev_start != AV_NOPTS_VALUE) {
 +        sub->end_display_time = av_rescale_q((sub->pts - ctx->prev_start ), AV_TIME_BASE_Q, (AVRational){ 1, 1000 }) - 1;
 +        *got_output = 1;
 +    }
 +    if (sub->num_rects > 0) {
 +
 +        sub->rects = av_mallocz_array(sizeof(*sub->rects), sub->num_rects);
 +        if (!sub->rects) {
 +            ret = AVERROR(ENOMEM);
 +            goto fail;
 +        }
 +
 +        for(i=0; i<sub->num_rects; i++)
 +            sub->rects[i] = av_mallocz(sizeof(*sub->rects[i]));
 +
 +        i = 0;
  
 +        for (display = ctx->display_list; display; display = display->next) {
 +            region = get_region(ctx, display->region_id);
 +
 +            if (!region)
 +                continue;
 +
 +            if (!region->dirty)
 +                continue;
 +
 +            rect = sub->rects[i];
 +            rect->x = display->x_pos + offset_x;
 +            rect->y = display->y_pos + offset_y;
 +            rect->w = region->width;
 +            rect->h = region->height;
 +            rect->nb_colors = (1 << region->depth);
 +            rect->type      = SUBTITLE_BITMAP;
 +            rect->linesize[0] = region->width;
 +
 +            clut = get_clut(ctx, region->clut);
 +
 +            if (!clut)
 +                clut = &default_clut;
 +
 +            switch (region->depth) {
 +            case 2:
 +                clut_table = clut->clut4;
 +                break;
 +            case 8:
 +                clut_table = clut->clut256;
 +                break;
 +            case 4:
 +            default:
 +                clut_table = clut->clut16;
 +                break;
 +            }
 +
 +            rect->data[1] = av_mallocz(AVPALETTE_SIZE);
 +            if (!rect->data[1]) {
 +                ret = AVERROR(ENOMEM);
 +                goto fail;
 +            }
 +            memcpy(rect->data[1], clut_table, (1 << region->depth) * sizeof(uint32_t));
 +
 +            rect->data[0] = av_malloc(region->buf_size);
 +            if (!rect->data[0]) {
 +                ret = AVERROR(ENOMEM);
 +                goto fail;
 +            }
 +
 +            memcpy(rect->data[0], region->pbuf, region->buf_size);
 +
 +            if ((clut == &default_clut && ctx->compute_clut == -1) || ctx->compute_clut == 1)
 +                compute_default_clut(rect, rect->w, rect->h);
 +
 +#if FF_API_AVPICTURE
 +FF_DISABLE_DEPRECATION_WARNINGS
 +{
 +            int j;
 +            for (j = 0; j < 4; j++) {
 +                rect->pict.data[j] = rect->data[j];
 +                rect->pict.linesize[j] = rect->linesize[j];
 +            }
 +}
 +FF_ENABLE_DEPRECATION_WARNINGS
 +#endif
 +
 +            i++;
 +        }
 +    }
 +
 +    return 0;
 +fail:
 +    if (sub->rects) {
 +        for(i=0; i<sub->num_rects; i++) {
 +            rect = sub->rects[i];
 +            if (rect) {
 +                av_freep(&rect->data[0]);
 +                av_freep(&rect->data[1]);
 +            }
 +            av_freep(&sub->rects[i]);
 +        }
 +        av_freep(&sub->rects);
 +    }
 +    sub->num_rects = 0;
 +    return ret;
 +}
  
  static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDisplay *display,
                                            const uint8_t *buf, int buf_size, int top_bottom, int non_mod)
                           0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
      uint8_t *map_table;
  
 +#if 0
      ff_dlog(avctx, "DVB pixel block size %d, %s field:\n", buf_size,
              top_bottom ? "bottom" : "top");
  
  
      if (i % 16)
          ff_dlog(avctx, "\n");
 +#endif
  
 -    if (region == 0)
 +    if (!region)
          return;
  
      pbuf = region->pbuf;
 +    region->dirty = 1;
  
      x_pos = display->x_pos;
      y_pos = display->y_pos;
  
 -    if ((y_pos & 1) != top_bottom)
 -        y_pos++;
 +    y_pos += top_bottom;
  
      while (buf < buf_end) {
 -        if (x_pos > region->width || y_pos > region->height) {
 -            av_log(avctx, AV_LOG_ERROR, "Invalid object location!\n");
 +        if ((*buf!=0xf0 && x_pos >= region->width) || y_pos >= region->height) {
 +            av_log(avctx, AV_LOG_ERROR, "Invalid object location! %d-%d %d-%d %02x\n", x_pos, region->width, y_pos, region->height, *buf);
              return;
          }
  
              else
                  map_table = NULL;
  
 -            x_pos += dvbsub_read_2bit_string(pbuf + (y_pos * region->width) + x_pos,
 -                                                region->width - x_pos, &buf, buf_end - buf,
 -                                                non_mod, map_table);
 +            x_pos = dvbsub_read_2bit_string(avctx, pbuf + (y_pos * region->width),
 +                                            region->width, &buf, buf_end - buf,
 +                                            non_mod, map_table, x_pos);
              break;
          case 0x11:
              if (region->depth < 4) {
              else
                  map_table = NULL;
  
 -            x_pos += dvbsub_read_4bit_string(pbuf + (y_pos * region->width) + x_pos,
 -                                                region->width - x_pos, &buf, buf_end - buf,
 -                                                non_mod, map_table);
 +            x_pos = dvbsub_read_4bit_string(avctx, pbuf + (y_pos * region->width),
 +                                            region->width, &buf, buf_end - buf,
 +                                            non_mod, map_table, x_pos);
              break;
          case 0x12:
              if (region->depth < 8) {
                  return;
              }
  
 -            x_pos += dvbsub_read_8bit_string(pbuf + (y_pos * region->width) + x_pos,
 -                                                region->width - x_pos, &buf, buf_end - buf,
 -                                                non_mod, NULL);
 +            x_pos = dvbsub_read_8bit_string(avctx, pbuf + (y_pos * region->width),
 +                                            region->width, &buf, buf_end - buf,
 +                                            non_mod, NULL, x_pos);
              break;
  
          case 0x20:
@@@ -1019,6 -787,7 +1025,6 @@@ static int dvbsub_parse_object_segment(
      DVBSubContext *ctx = avctx->priv_data;
  
      const uint8_t *buf_end = buf + buf_size;
 -    const uint8_t *block;
      int object_id;
      DVBSubObject *object;
      DVBSubObjectDisplay *display;
          buf += 2;
  
          if (buf + top_field_len + bottom_field_len > buf_end) {
 -            av_log(avctx, AV_LOG_ERROR, "Field data size too large\n");
 +            av_log(avctx, AV_LOG_ERROR, "Field data size %d+%d too large\n", top_field_len, bottom_field_len);
              return AVERROR_INVALIDDATA;
          }
  
          for (display = object->display_list; display; display = display->object_list_next) {
 -            block = buf;
 +            const uint8_t *block = buf;
 +            int bfl = bottom_field_len;
  
              dvbsub_parse_pixel_data_block(avctx, display, block, top_field_len, 0,
                                              non_modifying_color);
              if (bottom_field_len > 0)
                  block = buf + top_field_len;
              else
 -                bottom_field_len = top_field_len;
 +                bfl = top_field_len;
  
 -            dvbsub_parse_pixel_data_block(avctx, display, block, bottom_field_len, 1,
 +            dvbsub_parse_pixel_data_block(avctx, display, block, bfl, 1,
                                              non_modifying_color);
          }
  
@@@ -1080,7 -848,6 +1086,7 @@@ static int dvbsub_parse_clut_segment(AV
  
      const uint8_t *buf_end = buf + buf_size;
      int i, clut_id;
 +    int version;
      DVBSubCLUT *clut;
      int entry_id, depth , full_range;
      int y, cr, cb, alpha;
          ff_dlog(avctx, "\n");
  
      clut_id = *buf++;
 +    version = ((*buf)>>4)&15;
      buf += 1;
  
      clut = get_clut(ctx, clut_id);
          memcpy(clut, &default_clut, sizeof(DVBSubCLUT));
  
          clut->id = clut_id;
 +        clut->version = -1;
  
          clut->next = ctx->clut_list;
          ctx->clut_list = clut;
      }
  
 +    if (clut->version != version) {
 +
 +    clut->version = version;
 +
      while (buf + 4 < buf_end) {
          entry_id = *buf++;
  
  
          if (depth == 0) {
              av_log(avctx, AV_LOG_ERROR, "Invalid clut depth 0x%x!\n", *buf);
 -            return AVERROR_INVALIDDATA;
          }
  
          full_range = (*buf++) & 1;
          YUV_TO_RGB2_CCIR(r, g, b, y);
  
          ff_dlog(avctx, "clut %d := (%d,%d,%d,%d)\n", entry_id, r, g, b, alpha);
 +        if (!!(depth & 0x80) + !!(depth & 0x40) + !!(depth & 0x20) > 1) {
 +            ff_dlog(avctx, "More than one bit level marked: %x\n", depth);
 +            if (avctx->strict_std_compliance > FF_COMPLIANCE_NORMAL)
 +                return AVERROR_INVALIDDATA;
 +        }
  
          if (depth & 0x80)
              clut->clut4[entry_id] = RGBA(r,g,b,255 - alpha);
 -        if (depth & 0x40)
 +        else if (depth & 0x40)
              clut->clut16[entry_id] = RGBA(r,g,b,255 - alpha);
 -        if (depth & 0x20)
 +        else if (depth & 0x20)
              clut->clut256[entry_id] = RGBA(r,g,b,255 - alpha);
      }
 +    }
  
      return 0;
  }
@@@ -1179,7 -935,6 +1185,7 @@@ static int dvbsub_parse_region_segment(
  
      const uint8_t *buf_end = buf + buf_size;
      int region_id, object_id;
 +    int av_unused version;
      DVBSubRegion *region;
      DVBSubObject *object;
      DVBSubObjectDisplay *display;
              return AVERROR(ENOMEM);
  
          region->id = region_id;
 +        region->version = -1;
  
          region->next = ctx->region_list;
          ctx->region_list = region;
      }
  
 +    version = ((*buf)>>4) & 15;
      fill = ((*buf++) >> 3) & 1;
  
      region->width = AV_RB16(buf);
          region->buf_size = region->width * region->height;
  
          region->pbuf = av_malloc(region->buf_size);
 -        if (!region->pbuf)
 +        if (!region->pbuf) {
 +            region->buf_size =
 +            region->width =
 +            region->height = 0;
              return AVERROR(ENOMEM);
 +        }
  
          fill = 1;
 +        region->dirty = 0;
      }
  
      region->depth = 1 << (((*buf++) >> 2) & 7);
      }
      region->clut = *buf++;
  
 -    if (region->depth == 8)
 +    if (region->depth == 8) {
          region->bgcolor = *buf++;
 -    else {
 +        buf += 1;
 +    } else {
          buf += 1;
  
          if (region->depth == 4)
  }
  
  static int dvbsub_parse_page_segment(AVCodecContext *avctx,
 -                                     const uint8_t *buf, int buf_size)
 +                                     const uint8_t *buf, int buf_size, AVSubtitle *sub, int *got_output)
  {
      DVBSubContext *ctx = avctx->priv_data;
      DVBSubRegionDisplay *display;
      const uint8_t *buf_end = buf + buf_size;
      int region_id;
      int page_state;
 +    int timeout;
 +    int version;
  
      if (buf_size < 1)
          return AVERROR_INVALIDDATA;
  
 -    ctx->time_out = *buf++;
 +    timeout = *buf++;
 +    version = ((*buf)>>4) & 15;
      page_state = ((*buf++) >> 2) & 3;
  
 +    if (ctx->version == version) {
 +        return 0;
 +    }
 +
 +    ctx->time_out = timeout;
 +    ctx->version = version;
 +
      ff_dlog(avctx, "Page time out %ds, state %d\n", ctx->time_out, page_state);
  
 -    if (page_state == 2) {
 -        delete_state(ctx);
 +    if(ctx->compute_edt == 1)
 +        save_subtitle_set(avctx, sub, got_output);
 +
 +    if (page_state == 1 || page_state == 2) {
 +        delete_regions(ctx);
 +        delete_objects(ctx);
 +        delete_cluts(ctx);
      }
  
      tmp_display_list = ctx->display_list;
      ctx->display_list = NULL;
 -    ctx->display_list_size = 0;
  
      while (buf + 5 < buf_end) {
          region_id = *buf++;
  
          display->next = ctx->display_list;
          ctx->display_list = display;
 -        ctx->display_list_size++;
  
          ff_dlog(avctx, "Region %d, (%d,%d)\n", region_id, display->x_pos, display->y_pos);
      }
  
          tmp_display_list = display->next;
  
 -        av_free(display);
 +        av_freep(&display);
      }
  
      return 0;
@@@ -1409,9 -1143,6 +1415,9 @@@ static int save_display_set(DVBSubConte
      for (display = ctx->display_list; display; display = display->next) {
          region = get_region(ctx, display->region_id);
  
 +        if (!region)
 +            return -1;
 +
          if (x_pos == -1) {
              x_pos = display->x_pos;
              y_pos = display->y_pos;
  
          pbuf = av_malloc(width * height * 4);
          if (!pbuf)
 -            return AVERROR(ENOMEM);
 +            return -1;
  
          for (display = ctx->display_list; display; display = display->next) {
              region = get_region(ctx, display->region_id);
  
 +            if (!region)
 +                return -1;
 +
              x_off = display->x_pos - x_pos;
              y_off = display->y_pos - y_pos;
  
              clut = get_clut(ctx, region->clut);
  
 -            if (clut == 0)
 +            if (!clut)
                  clut = &default_clut;
  
              switch (region->depth) {
  
          png_save(filename, pbuf, width, height);
  
 -        av_free(pbuf);
 +        av_freep(&pbuf);
      }
  
      fileno_index++;
@@@ -1520,18 -1248,14 +1526,18 @@@ static int dvbsub_parse_display_definit
      display_def->y       = 0;
      display_def->width   = bytestream_get_be16(&buf) + 1;
      display_def->height  = bytestream_get_be16(&buf) + 1;
 -
 -    if (buf_size < 13)
 -        return AVERROR_INVALIDDATA;
 +    if (!avctx->width || !avctx->height) {
 +        avctx->width  = display_def->width;
 +        avctx->height = display_def->height;
 +    }
  
      if (info_byte & 1<<3) { // display_window_flag
 +        if (buf_size < 13)
 +            return AVERROR_INVALIDDATA;
 +
          display_def->x = bytestream_get_be16(&buf);
 -        display_def->y = bytestream_get_be16(&buf);
          display_def->width  = bytestream_get_be16(&buf) - display_def->x + 1;
 +        display_def->y = bytestream_get_be16(&buf);
          display_def->height = bytestream_get_be16(&buf) - display_def->y + 1;
      }
  
  }
  
  static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf,
 -                                      int buf_size, AVSubtitle *sub)
 +                                      int buf_size, AVSubtitle *sub,int *got_output)
  {
      DVBSubContext *ctx = avctx->priv_data;
 -    DVBSubDisplayDefinition *display_def = ctx->display_definition;
 -
 -    DVBSubRegion *region;
 -    DVBSubRegionDisplay *display;
 -    AVSubtitleRect *rect;
 -    DVBSubCLUT *clut;
 -    uint32_t *clut_table;
 -    int i;
 -    int offset_x=0, offset_y=0;
 -
 -    sub->rects = NULL;
 -    sub->start_display_time = 0;
 -    sub->end_display_time = ctx->time_out * 1000;
 -    sub->format = 0;
 -
 -    if (display_def) {
 -        offset_x = display_def->x;
 -        offset_y = display_def->y;
 -    }
 -
 -    sub->num_rects = ctx->display_list_size;
 -    if (sub->num_rects <= 0)
 -        return AVERROR_INVALIDDATA;
 -
 -    sub->rects = av_mallocz_array(sub->num_rects * sub->num_rects,
 -                                  sizeof(*sub->rects));
 -    if (!sub->rects)
 -        return AVERROR(ENOMEM);
 -
 -    i = 0;
 -
 -    for (display = ctx->display_list; display; display = display->next) {
 -        region = get_region(ctx, display->region_id);
 -        rect = sub->rects[i];
 -
 -        if (!region)
 -            continue;
 -
 -        rect->x = display->x_pos + offset_x;
 -        rect->y = display->y_pos + offset_y;
 -        rect->w = region->width;
 -        rect->h = region->height;
 -        rect->nb_colors = 16;
 -        rect->type      = SUBTITLE_BITMAP;
 -        rect->linesize[0] = region->width;
 -
 -        clut = get_clut(ctx, region->clut);
 -
 -        if (!clut)
 -            clut = &default_clut;
 -
 -        switch (region->depth) {
 -        case 2:
 -            clut_table = clut->clut4;
 -            break;
 -        case 8:
 -            clut_table = clut->clut256;
 -            break;
 -        case 4:
 -        default:
 -            clut_table = clut->clut16;
 -            break;
 -        }
 -
 -        rect->data[1] = av_mallocz(AVPALETTE_SIZE);
 -        if (!rect->data[1]) {
 -            av_free(sub->rects);
 -            return AVERROR(ENOMEM);
 -        }
 -        memcpy(rect->data[1], clut_table, (1 << region->depth) * sizeof(uint32_t));
 -
 -        rect->data[0] = av_malloc(region->buf_size);
 -        if (!rect->data[0]) {
 -            av_free(rect->data[1]);
 -            av_free(sub->rects);
 -            return AVERROR(ENOMEM);
 -        }
 -        memcpy(rect->data[0], region->pbuf, region->buf_size);
 -
 -#if FF_API_AVPICTURE
 -FF_DISABLE_DEPRECATION_WARNINGS
 -{
 -        int j;
 -        for (j = 0; j < 4; j++) {
 -            rect->pict.data[j] = rect->data[j];
 -            rect->pict.linesize[j] = rect->linesize[j];
 -        }
 -}
 -FF_ENABLE_DEPRECATION_WARNINGS
 -#endif
 -
 -        i++;
 -    }
 -
 -    sub->num_rects = i;
  
 +    if(ctx->compute_edt == 0)
 +        save_subtitle_set(avctx, sub, got_output);
  #ifdef DEBUG
      save_display_set(ctx);
  #endif
 -
 -    return 1;
 +    return 0;
  }
  
  static int dvbsub_decode(AVCodecContext *avctx,
      int page_id;
      int segment_length;
      int i;
 +    int ret = 0;
 +    int got_segment = 0;
 +    int got_dds = 0;
  
      ff_dlog(avctx, "DVB sub packet:\n");
  
          segment_length = AV_RB16(p);
          p += 2;
  
 +        if (avctx->debug & FF_DEBUG_STARTCODE) {
 +            av_log(avctx, AV_LOG_DEBUG, "segment_type:%d page_id:%d segment_length:%d\n", segment_type, page_id, segment_length);
 +        }
 +
          if (p_end - p < segment_length) {
              ff_dlog(avctx, "incomplete or broken packet");
 -            return -1;
 +            ret = -1;
 +            goto end;
          }
  
          if (page_id == ctx->composition_id || page_id == ctx->ancillary_id ||
              int ret = 0;
              switch (segment_type) {
              case DVBSUB_PAGE_SEGMENT:
 -                ret = dvbsub_parse_page_segment(avctx, p, segment_length);
 +                ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, data_size);
 +                got_segment |= 1;
                  break;
              case DVBSUB_REGION_SEGMENT:
                  ret = dvbsub_parse_region_segment(avctx, p, segment_length);
 +                got_segment |= 2;
                  break;
              case DVBSUB_CLUT_SEGMENT:
                  ret = dvbsub_parse_clut_segment(avctx, p, segment_length);
 +                if (ret < 0) goto end;
 +                got_segment |= 4;
                  break;
              case DVBSUB_OBJECT_SEGMENT:
                  ret = dvbsub_parse_object_segment(avctx, p, segment_length);
 +                got_segment |= 8;
                  break;
              case DVBSUB_DISPLAYDEFINITION_SEGMENT:
                  ret = dvbsub_parse_display_definition_segment(avctx, p,
                                                                segment_length);
 +                got_dds = 1;
                  break;
              case DVBSUB_DISPLAY_SEGMENT:
 -                ret = dvbsub_display_end_segment(avctx, p, segment_length, sub);
 -                *data_size = ret;
 +                ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, data_size);
 +                if (got_segment == 15 && !got_dds && !avctx->width && !avctx->height) {
 +                    // Default from ETSI EN 300 743 V1.3.1 (7.2.1)
 +                    avctx->width  = 720;
 +                    avctx->height = 576;
 +                }
 +                got_segment |= 16;
                  break;
              default:
                  ff_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n",
                  break;
              }
              if (ret < 0)
 -                return ret;
 +                goto end;
          }
  
          p += segment_length;
      }
 +    // Some streams do not send a display segment but if we have all the other
 +    // segments then we need no further data.
 +    if (got_segment == 15) {
 +        av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n");
 +        dvbsub_display_end_segment(avctx, p, 0, sub, data_size);
 +    }
 +
 +end:
 +    if(ret < 0) {
 +        *data_size = 0;
 +        avsubtitle_free(sub);
 +        return ret;
 +    } else {
 +        if(ctx->compute_edt == 1 )
 +            FFSWAP(int64_t, ctx->prev_start, sub->pts);
 +    }
  
      return p - buf;
  }
  
 +#define DS AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM
 +static const AVOption options[] = {
 +    {"compute_edt", "compute end of time using pts or timeout", offsetof(DVBSubContext, compute_edt), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DS},
 +    {"compute_clut", "compute clut when not available(-1) or always(1) or never(0)", offsetof(DVBSubContext, compute_clut), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, DS},
 +    {"dvb_substream", "", offsetof(DVBSubContext, substream), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 63, DS},
 +    {NULL}
 +};
 +static const AVClass dvbsubdec_class = {
 +    .class_name = "DVB Sub Decoder",
 +    .item_name  = av_default_item_name,
 +    .option     = options,
 +    .version    = LIBAVUTIL_VERSION_INT,
 +};
  
  AVCodec ff_dvbsub_decoder = {
      .name           = "dvbsub",
      .init           = dvbsub_init_decoder,
      .close          = dvbsub_close_decoder,
      .decode         = dvbsub_decode,
 +    .priv_class     = &dvbsubdec_class,
  };