Merge commit 'a17a7661906ba295d67afd80ac0770422e1b02b3'
authorHendrik Leppkes <h.leppkes@gmail.com>
Thu, 22 Oct 2015 19:18:03 +0000 (21:18 +0200)
committerHendrik Leppkes <h.leppkes@gmail.com>
Thu, 22 Oct 2015 19:41:53 +0000 (21:41 +0200)
* commit 'a17a7661906ba295d67afd80ac0770422e1b02b3':
  lavc: Add data and linesize to AVSubtitleRect

Merged-by: Hendrik Leppkes <h.leppkes@gmail.com>
1  2 
doc/APIchanges
libavcodec/avcodec.h
libavcodec/dvbsub.c
libavcodec/dvbsubdec.c
libavcodec/dvdsubdec.c
libavcodec/dvdsubenc.c
libavcodec/pgssubdec.c
libavcodec/utils.c
libavcodec/version.h
libavcodec/xsubdec.c
libavcodec/xsubenc.c

diff --cc doc/APIchanges
@@@ -15,13 -13,14 +15,17 @@@ libavutil:     2015-08-2
  
  API changes, most recent first:
  
 -2015-xx-xx - xxxxxxx - lavc 57.5.0 - avcodec.h
++2015-10-22 - xxxxxxx - lavc 57.9.100 / lavc 57.5.0 - avcodec.h
+   Add data and linesize array to AVSubtitleRect, to be used instead of
+   the ones from the embedded AVPicture.
 -2015-xx-xx - xxxxxxx - lavc 57.0.0 - qsv.h
 +2015-10-22 - xxxxxxx - lavc 57.8.100 / lavc 57.0.0 - qsv.h
    Add an API for allocating opaque surfaces.
  
 -2015-xx-xx - xxxxxxx - lavu 55.2.0 - dict.h
 +2015-10-15 - xxxxxxx - lavf 57.4.100
 +  Remove the latm demuxer that was a duplicate of the loas demuxer.
 +
 +2015-10-14 - xxxxxxx - lavu 55.4.100 / lavu 55.2.0 - dict.h
    Change return type of av_dict_copy() from void to int, so that a proper
    error code can be reported.
  
Simple merge
@@@ -409,11 -353,16 +409,11 @@@ static int encode_dvb_subtitles(DVBSubt
                  pbottom_field_len = q;
                  q += 2;
  
 -                if (bpp_index == 0)
 -                    dvb_encode_rle = dvb_encode_rle2;
 -                else
 -                    dvb_encode_rle = dvb_encode_rle4;
 -
                  top_ptr = q;
-                 dvb_encode_rle(&q, h->rects[object_id]->pict.data[0], h->rects[object_id]->w * 2,
+                 dvb_encode_rle(&q, h->rects[object_id]->data[0], h->rects[object_id]->w * 2,
                                      h->rects[object_id]->w, h->rects[object_id]->h >> 1);
                  bottom_ptr = q;
-                 dvb_encode_rle(&q, h->rects[object_id]->pict.data[0] + h->rects[object_id]->w,
+                 dvb_encode_rle(&q, h->rects[object_id]->data[0] + h->rects[object_id]->w,
                                      h->rects[object_id]->w * 2, h->rects[object_id]->w,
                                      h->rects[object_id]->h >> 1);
  
@@@ -761,187 -717,7 +761,196 @@@ static int dvbsub_read_8bit_string(AVCo
      return pixels_read;
  }
  
- static void compute_default_clut(AVPicture *frame, int w, int h)
++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) frame->data[0][(x) + (y)*frame->linesize[0]]
++#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[ frame->data[0][(x) + (y)*frame->linesize[0]] ]
++#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 = frame->data[0][x + y*frame->linesize[0]];
++                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;
 +    }
  
-         AV_WN32(frame->data[1] + 4*list_inv[i], RGBA(v/2,v,v/2,v));
 +    count = i - 1;
 +    for (i--; i>=0; i--) {
 +        int v = i*255/count;
-     int i;
++        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;
-             rect->pict.linesize[0] = region->width;
++    int i,j;
 +    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->pict.data[1] = av_mallocz(AVPALETTE_SIZE);
-             if (!rect->pict.data[1]) {
++            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;
 +            }
 +
-             memcpy(rect->pict.data[1], clut_table, (1 << region->depth) * sizeof(uint32_t));
++            rect->data[1] = av_mallocz(AVPALETTE_SIZE);
++            if (!rect->data[1]) {
 +                ret = AVERROR(ENOMEM);
 +                goto fail;
 +            }
-             rect->pict.data[0] = av_malloc(region->buf_size);
-             if (!rect->pict.data[0]) {
++            memcpy(rect->data[1], clut_table, (1 << region->depth) * sizeof(uint32_t));
 +
-             memcpy(rect->pict.data[0], region->pbuf, region->buf_size);
++            rect->data[0] = av_malloc(region->buf_size);
++            if (!rect->data[0]) {
 +                ret = AVERROR(ENOMEM);
 +                goto fail;
 +            }
 +
-                 compute_default_clut(&rect->pict, rect->w, rect->h);
++            memcpy(rect->data[0], region->pbuf, region->buf_size);
 +
 +            if ((clut == &default_clut && ctx->compute_clut == -1) || ctx->compute_clut == 1)
-                 av_freep(&rect->pict.data[0]);
-                 av_freep(&rect->pict.data[1]);
++                compute_default_clut(rect, rect->w, rect->h);
++
++#if FF_API_AVPICTURE
++FF_DISABLE_DEPRECATION_WARNINGS
++            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)
@@@ -200,21 -173,6 +200,21 @@@ static void guess_palette(DVDSubContext
      }
  }
  
-             av_freep(&sub_header->rects[i]->pict.data[0]);
-             av_freep(&sub_header->rects[i]->pict.data[1]);
 +static void reset_rects(AVSubtitle *sub_header)
 +{
 +    int i;
 +
 +    if (sub_header->rects) {
 +        for (i = 0; i < sub_header->num_rects; i++) {
++            av_freep(&sub_header->rects[i]->data[0]);
++            av_freep(&sub_header->rects[i]->data[1]);
 +            av_freep(&sub_header->rects[i]);
 +        }
 +        av_freep(&sub_header->rects);
 +        sub_header->num_rects = 0;
 +    }
 +}
 +
  #define READ_OFFSET(a) (big_offsets ? AV_RB32(a) : AV_RB16(a))
  
  static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
                  if (!sub_header->rects[0])
                      goto fail;
                  sub_header->num_rects = 1;
-                 bitmap = sub_header->rects[0]->pict.data[0] = av_malloc(w * h);
+                 bitmap = sub_header->rects[0]->data[0] = av_malloc(w * h);
                  if (!bitmap)
                      goto fail;
 -                decode_rle(bitmap, w * 2, w, (h + 1) / 2,
 -                           buf, offset1, buf_size, is_8bit);
 -                decode_rle(bitmap + w, w * 2, w, h / 2,
 -                           buf, offset2, buf_size, is_8bit);
 +                if (decode_rle(bitmap, w * 2, w, (h + 1) / 2,
 +                               buf, offset1, buf_size, is_8bit) < 0)
 +                    goto fail;
 +                if (decode_rle(bitmap + w, w * 2, w, h / 2,
 +                               buf, offset2, buf_size, is_8bit) < 0)
 +                    goto fail;
-                 sub_header->rects[0]->pict.data[1] = av_mallocz(AVPALETTE_SIZE);
-                 if (!sub_header->rects[0]->pict.data[1])
+                 sub_header->rects[0]->data[1] = av_mallocz(AVPALETTE_SIZE);
+                 if (!sub_header->rects[0]->data[1])
                      goto fail;
                  if (is_8bit) {
 -                    if (yuv_palette == 0)
 +                    if (!yuv_palette)
                          goto fail;
                      sub_header->rects[0]->nb_colors = 256;
-                     yuv_a_to_rgba(yuv_palette, alpha, (uint32_t*)sub_header->rects[0]->pict.data[1], 256);
+                     yuv_a_to_rgba(yuv_palette, alpha,
+                                   (uint32_t *)sub_header->rects[0]->data[1],
+                                   256);
                  } else {
                      sub_header->rects[0]->nb_colors = 4;
-                     guess_palette(ctx, (uint32_t*)sub_header->rects[0]->pict.data[1],
 -                    guess_palette(ctx,
 -                                  (uint32_t *)sub_header->rects[0]->data[1],
 -                                  colormap, alpha, 0xffff00);
++                    guess_palette(ctx, (uint32_t*)sub_header->rects[0]->data[1],
 +                                  0xffff00);
                  }
                  sub_header->rects[0]->x = x1;
                  sub_header->rects[0]->y = y1;
                  sub_header->rects[0]->w = w;
                  sub_header->rects[0]->h = h;
                  sub_header->rects[0]->type = SUBTITLE_BITMAP;
-                 sub_header->rects[0]->pict.linesize[0] = w;
+                 sub_header->rects[0]->linesize[0] = w;
 +                sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0;
+ #if FF_API_AVPICTURE
+ FF_DISABLE_DEPRECATION_WARNINGS
 -                rect = sub_header->rects[0];
 -                for (j = 0; j < 4; j++) {
 -                    rect->pict.data[j] = rect->data[j];
 -                    rect->pict.linesize[j] = rect->linesize[j];
++                for (i = 0; i < 4; i++) {
++                    sub_header->rects[0]->pict.data[i] = sub_header->rects[0]->data[i];
++                    sub_header->rects[0]->pict.linesize[i] = sub_header->rects[0]->linesize[i];
+                 }
+ FF_ENABLE_DEPRECATION_WARNINGS
+ #endif
              }
          }
 +        if (next_cmd_pos < cmd_pos) {
 +            av_log(ctx, AV_LOG_ERROR, "Invalid command offset\n");
 +            break;
 +        }
          if (next_cmd_pos == cmd_pos)
              break;
          cmd_pos = next_cmd_pos;
@@@ -483,6 -456,6 +494,16 @@@ static int find_smallest_bounding_recta
      s->rects[0]->h = h;
      s->rects[0]->x += x1;
      s->rects[0]->y += y1;
++
++#if FF_API_AVPICTURE
++FF_DISABLE_DEPRECATION_WARNINGS
++    for (i = 0; i < 4; i++) {
++        s->rects[0]->pict.data[i] = s->rects[0]->data[i];
++        s->rects[0]->pict.linesize[i] = s->rects[0]->linesize[i];
++    }
++FF_ENABLE_DEPRECATION_WARNINGS
++#endif
++
      return 1;
  }
  
@@@ -583,12 -512,10 +604,12 @@@ static int dvdsub_decode(AVCodecContex
      ff_dlog(NULL, "start=%d ms end =%d ms\n",
              sub->start_display_time,
              sub->end_display_time);
-     ppm_save(ppm_name, sub->rects[0]->pict.data[0],
-              sub->rects[0]->w, sub->rects[0]->h, (uint32_t*) sub->rects[0]->pict.data[1]);
 -    ppm_save("/tmp/a.ppm", sub->rects[0]->data[0],
 -             sub->rects[0]->w, sub->rects[0]->h, sub->rects[0]->data[1]);
++    ppm_save(ppm_name, sub->rects[0]->data[0],
++             sub->rects[0]->w, sub->rects[0]->h, (uint32_t*) sub->rects[0]->data[1]);
 +    }
  #endif
  
 +    ctx->buf_size = 0;
      *data_size = 1;
      return buf_size;
  }
@@@ -94,265 -86,86 +94,280 @@@ static void dvd_encode_rle(uint8_t **pq
      *pq = q;
  }
  
 -static int encode_dvd_subtitles(uint8_t *outbuf, int outbuf_size,
 +static int color_distance(uint32_t a, uint32_t b)
 +{
 +    int r = 0, d, i;
 +    int alpha_a = 8, alpha_b = 8;
 +
 +    for (i = 24; i >= 0; i -= 8) {
 +        d = alpha_a * (int)((a >> i) & 0xFF) -
 +            alpha_b * (int)((b >> i) & 0xFF);
 +        r += d * d;
 +        alpha_a = a >> 28;
 +        alpha_b = b >> 28;
 +    }
 +    return r;
 +}
 +
 +/**
 + * Count colors used in a rectangle, quantizing alpha and grouping by
 + * nearest global palette entry.
 + */
 +static void count_colors(AVCodecContext *avctx, unsigned hits[33],
 +                         const AVSubtitleRect *r)
 +{
 +    DVDSubtitleContext *dvdc = avctx->priv_data;
 +    unsigned count[256] = { 0 };
-     uint32_t *palette = (uint32_t *)r->pict.data[1];
++    uint32_t *palette = (uint32_t *)r->data[1];
 +    uint32_t color;
 +    int x, y, i, j, match, d, best_d, av_uninit(best_j);
-     uint8_t *p = r->pict.data[0];
++    uint8_t *p = r->data[0];
 +
 +    for (y = 0; y < r->h; y++) {
 +        for (x = 0; x < r->w; x++)
 +            count[*(p++)]++;
-         p += r->pict.linesize[0] - r->w;
++        p += r->linesize[0] - r->w;
 +    }
 +    for (i = 0; i < 256; i++) {
 +        if (!count[i]) /* avoid useless search */
 +            continue;
 +        color = palette[i];
 +        /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
 +        match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17;
 +        if (match) {
 +            best_d = INT_MAX;
 +            for (j = 0; j < 16; j++) {
 +                d = color_distance(0xFF000000 | color,
 +                                   0xFF000000 | dvdc->global_palette[j]);
 +                if (d < best_d) {
 +                    best_d = d;
 +                    best_j = j;
 +                }
 +            }
 +            match += best_j;
 +        }
 +        hits[match] += count[i];
 +    }
 +}
 +
 +static void select_palette(AVCodecContext *avctx, int out_palette[4],
 +                           int out_alpha[4], unsigned hits[33])
 +{
 +    DVDSubtitleContext *dvdc = avctx->priv_data;
 +    int i, j, bright, mult;
 +    uint32_t color;
 +    int selected[4] = { 0 };
 +    uint32_t pseudopal[33] = { 0 };
 +    uint32_t refcolor[3] = { 0x00000000, 0xFFFFFFFF, 0xFF000000 };
 +
 +    /* Bonus for transparent: if the rectangle fits tightly the text, the
 +       background color can be quite rare, but it would be ugly without it */
 +    hits[0] *= 16;
 +    /* Bonus for bright colors */
 +    for (i = 0; i < 16; i++) {
 +        if (!(hits[1 + i] + hits[17 + i]))
 +            continue; /* skip unused colors to gain time */
 +        color = dvdc->global_palette[i];
 +        bright = 0;
 +        for (j = 0; j < 3; j++, color >>= 8)
 +            bright += (color & 0xFF) < 0x40 || (color & 0xFF) >= 0xC0;
 +        mult = 2 + FFMIN(bright, 2);
 +        hits[ 1 + i] *= mult;
 +        hits[17 + i] *= mult;
 +    }
 +
 +    /* Select four most frequent colors */
 +    for (i = 0; i < 4; i++) {
 +        for (j = 0; j < 33; j++)
 +            if (hits[j] > hits[selected[i]])
 +                selected[i] = j;
 +        hits[selected[i]] = 0;
 +    }
 +
 +    /* Order the colors like in most DVDs:
 +       0: background, 1: foreground, 2: outline */
 +    for (i = 0; i < 16; i++) {
 +        pseudopal[ 1 + i] = 0x80000000 | dvdc->global_palette[i];
 +        pseudopal[17 + i] = 0xFF000000 | dvdc->global_palette[i];
 +    }
 +    for (i = 0; i < 3; i++) {
 +        int best_d = color_distance(refcolor[i], pseudopal[selected[i]]);
 +        for (j = i + 1; j < 4; j++) {
 +            int d = color_distance(refcolor[i], pseudopal[selected[j]]);
 +            if (d < best_d) {
 +                FFSWAP(int, selected[i], selected[j]);
 +                best_d = d;
 +            }
 +        }
 +    }
 +
 +    /* Output */
 +    for (i = 0; i < 4; i++) {
 +        out_palette[i] = selected[i] ? (selected[i] - 1) & 0xF : 0;
 +        out_alpha  [i] = !selected[i] ? 0 : selected[i] < 17 ? 0x80 : 0xFF;
 +    }
 +}
 +
 +static void build_color_map(AVCodecContext *avctx, int cmap[],
 +                            const uint32_t palette[],
 +                            const int out_palette[], unsigned int const out_alpha[])
 +{
 +    DVDSubtitleContext *dvdc = avctx->priv_data;
 +    int i, j, d, best_d;
 +    uint32_t pseudopal[4];
 +
 +    for (i = 0; i < 4; i++)
 +        pseudopal[i] = (out_alpha[i] << 24) |
 +                       dvdc->global_palette[out_palette[i]];
 +    for (i = 0; i < 256; i++) {
 +        best_d = INT_MAX;
 +        for (j = 0; j < 4; j++) {
 +            d = color_distance(pseudopal[j], palette[i]);
 +            if (d < best_d) {
 +                cmap[i] = j;
 +                best_d = d;
 +            }
 +        }
 +    }
 +}
 +
 +static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[])
 +{
 +    int x, y;
 +    uint8_t *p, *q;
 +
-     p = src->pict.data[0];
-     q = dst->pict.data[0] + (src->x - dst->x) +
-                             (src->y - dst->y) * dst->pict.linesize[0];
++    p = src->data[0];
++    q = dst->data[0] + (src->x - dst->x) +
++                            (src->y - dst->y) * dst->linesize[0];
 +    for (y = 0; y < src->h; y++) {
 +        for (x = 0; x < src->w; x++)
 +            *(q++) = cmap[*(p++)];
-         p += src->pict.linesize[0] - src->w;
-         q += dst->pict.linesize[0] - src->w;
++        p += src->linesize[0] - src->w;
++        q += dst->linesize[0] - src->w;
 +    }
 +}
 +
 +static int encode_dvd_subtitles(AVCodecContext *avctx,
 +                                uint8_t *outbuf, int outbuf_size,
                                  const AVSubtitle *h)
  {
 +    DVDSubtitleContext *dvdc = avctx->priv_data;
      uint8_t *q, *qq;
 -    int object_id;
 -    int offset1[20], offset2[20];
 -    int i, imax, color, alpha, rects = h->num_rects;
 -    unsigned long hmax;
 -    unsigned long hist[256];
 -    int           cmap[256];
 +    int offset1, offset2;
 +    int i, rects = h->num_rects, ret;
 +    unsigned global_palette_hits[33] = { 0 };
 +    int cmap[256];
 +    int out_palette[4];
 +    int out_alpha[4];
 +    AVSubtitleRect vrect;
 +    uint8_t *vrect_data = NULL;
 +    int x2, y2;
 +    int forced = 0;
  
      if (rects == 0 || !h->rects)
 -        return -1;
 -    if (rects > 20)
 -        rects = 20;
 -
 -    // analyze bitmaps, compress to 4 colors
 -    for (i=0; i<256; ++i) {
 -        hist[i] = 0;
 -        cmap[i] = 0;
 -    }
 -    for (object_id = 0; object_id < rects; object_id++) {
 +        return AVERROR(EINVAL);
 +    for (i = 0; i < rects; i++)
 +        if (h->rects[i]->type != SUBTITLE_BITMAP) {
 +            av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n");
 +            return AVERROR(EINVAL);
 +        }
 +    /* Mark this subtitle forced if any of the rectangles is forced. */
 +    for (i = 0; i < rects; i++)
 +        if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) {
 +            forced = 1;
 +            break;
 +        }
++
+ #if FF_API_AVPICTURE
+ FF_DISABLE_DEPRECATION_WARNINGS
 -        if (!h->rects[object_id]->data[0]) {
 -            AVSubtitleRect *rect = h->rects[object_id];
++    for (i = 0; i < rects; i++)
++        if (!h->rects[i]->data[0]) {
++            AVSubtitleRect *rect = h->rects[i];
+             int j;
+             for (j = 0; j < 4; j++) {
+                 rect->data[j] = rect->pict.data[j];
+                 rect->linesize[j] = rect->pict.linesize[j];
+             }
+         }
+ FF_ENABLE_DEPRECATION_WARNINGS
+ #endif
 -        for (i=0; i<h->rects[object_id]->w*h->rects[object_id]->h; ++i) {
 -            color = h->rects[object_id]->data[0][i];
 -            // only count non-transparent pixels
 -            alpha = ((uint32_t *)h->rects[object_id]->data[1])[color] >> 24;
 -            hist[color] += alpha;
 +    vrect = *h->rects[0];
 +
 +    if (rects > 1) {
 +        /* DVD subtitles can have only one rectangle: build a virtual
 +           rectangle containing all actual rectangles.
 +           The data of the rectangles will be copied later, when the palette
 +           is decided, because the rectangles may have different palettes. */
 +        int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w;
 +        int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h;
 +        for (i = 1; i < rects; i++) {
 +            xmin = FFMIN(xmin, h->rects[i]->x);
 +            ymin = FFMIN(ymin, h->rects[i]->y);
 +            xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w);
 +            ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h);
          }
 +        vrect.x = xmin;
 +        vrect.y = ymin;
 +        vrect.w = xmax - xmin;
 +        vrect.h = ymax - ymin;
 +        if ((ret = av_image_check_size(vrect.w, vrect.h, 0, avctx)) < 0)
 +            return ret;
 +
 +        /* Count pixels outside the virtual rectangle as transparent */
 +        global_palette_hits[0] = vrect.w * vrect.h;
 +        for (i = 0; i < rects; i++)
 +            global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h;
      }
 -    for (color=3;; --color) {
 -        hmax = 0;
 -        imax = 0;
 -        for (i=0; i<256; ++i)
 -            if (hist[i] > hmax) {
 -                imax = i;
 -                hmax = hist[i];
 -            }
 -        if (hmax == 0)
 -            break;
 -        if (color == 0)
 -            color = 3;
 -        av_log(NULL, AV_LOG_DEBUG, "dvd_subtitle hist[%d]=%ld -> col %d\n",
 -               imax, hist[imax], color);
 -        cmap[imax] = color;
 -        hist[imax] = 0;
 +
 +    for (i = 0; i < rects; i++)
 +        count_colors(avctx, global_palette_hits, h->rects[i]);
 +    select_palette(avctx, out_palette, out_alpha, global_palette_hits);
 +
 +    if (rects > 1) {
 +        if (!(vrect_data = av_calloc(vrect.w, vrect.h)))
 +            return AVERROR(ENOMEM);
-         vrect.pict.data    [0] = vrect_data;
-         vrect.pict.linesize[0] = vrect.w;
++        vrect.data    [0] = vrect_data;
++        vrect.linesize[0] = vrect.w;
 +        for (i = 0; i < rects; i++) {
-             build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->pict.data[1],
++            build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1],
 +                            out_palette, out_alpha);
 +            copy_rectangle(&vrect, h->rects[i], cmap);
 +        }
 +        for (i = 0; i < 4; i++)
 +            cmap[i] = i;
 +    } else {
-         build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->pict.data[1],
++        build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1],
 +                        out_palette, out_alpha);
      }
  
 +    av_log(avctx, AV_LOG_DEBUG, "Selected palette:");
 +    for (i = 0; i < 4; i++)
 +        av_log(avctx, AV_LOG_DEBUG, " 0x%06x@@%02x (0x%x,0x%x)",
 +               dvdc->global_palette[out_palette[i]], out_alpha[i],
 +               out_palette[i], out_alpha[i] >> 4);
 +    av_log(avctx, AV_LOG_DEBUG, "\n");
  
      // encode data block
      q = outbuf + 4;
 -    for (object_id = 0; object_id < rects; object_id++) {
 -        offset1[object_id] = q - outbuf;
 -        // worst case memory requirement: 1 nibble per pixel..
 -        if ((q - outbuf) + h->rects[object_id]->w*h->rects[object_id]->h/2
 -            + 17*rects + 21 > outbuf_size) {
 -            av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n");
 -            return -1;
 -        }
 -        dvd_encode_rle(&q, h->rects[object_id]->data[0],
 -                       h->rects[object_id]->w*2,
 -                       h->rects[object_id]->w, h->rects[object_id]->h >> 1,
 -                       cmap);
 -        offset2[object_id] = q - outbuf;
 -        dvd_encode_rle(&q, h->rects[object_id]->data[0] + h->rects[object_id]->w,
 -                       h->rects[object_id]->w*2,
 -                       h->rects[object_id]->w, h->rects[object_id]->h >> 1,
 -                       cmap);
 +    offset1 = q - outbuf;
 +    // worst case memory requirement: 1 nibble per pixel..
 +    if ((q - outbuf) + vrect.w * vrect.h / 2 + 17 + 21 > outbuf_size) {
 +        av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n");
 +        ret = AVERROR_BUFFER_TOO_SMALL;
 +        goto fail;
 +    }
-     dvd_encode_rle(&q, vrect.pict.data[0], vrect.w * 2,
++    dvd_encode_rle(&q, vrect.data[0], vrect.w * 2,
 +                   vrect.w, (vrect.h + 1) >> 1, cmap);
 +    offset2 = q - outbuf;
-     dvd_encode_rle(&q, vrect.pict.data[0] + vrect.w, vrect.w * 2,
++    dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2,
 +                   vrect.w, vrect.h >> 1, cmap);
 +
 +    if (dvdc->even_rows_fix && (vrect.h & 1)) {
 +        // Work-around for some players that want the height to be even.
 +        vrect.h++;
 +        *q++ = 0x00; // 0x00 0x00 == empty row, i.e. fully transparent
 +        *q++ = 0x00;
      }
  
      // set data packet size
@@@ -166,9 -163,9 +166,9 @@@ static int decode_rle(AVCodecContext *a
  
      rle_bitmap_end = buf + buf_size;
  
-     rect->pict.data[0] = av_malloc_array(rect->w, rect->h);
 -    rect->data[0] = av_malloc(rect->w * rect->h);
++    rect->data[0] = av_malloc_array(rect->w, rect->h);
  
-     if (!rect->pict.data[0])
+     if (!rect->data[0])
          return AVERROR(ENOMEM);
  
      pixel_count = 0;
@@@ -553,8 -541,18 +555,8 @@@ static int display_end_segment(AVCodecC
          sub->rects[i]->w    = object->w;
          sub->rects[i]->h    = object->h;
  
-         sub->rects[i]->pict.linesize[0] = object->w;
+         sub->rects[i]->linesize[0] = object->w;
  
 -#if FF_API_AVPICTURE
 -FF_DISABLE_DEPRECATION_WARNINGS
 -        rect = sub->rects[i];
 -        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
 -
          if (object->rle) {
              if (object->rle_remaining_len) {
                  av_log(avctx, AV_LOG_ERROR, "RLE data length %u is %u bytes shorter than expected\n",
              return AVERROR(ENOMEM);
          }
  
-         memcpy(sub->rects[i]->pict.data[1], palette->clut, sub->rects[i]->nb_colors * sizeof(uint32_t));
 +        if (!ctx->forced_subs_only || ctx->presentation.objects[i].composition_flag & 0x40)
+         memcpy(sub->rects[i]->data[1], palette->clut, sub->rects[i]->nb_colors * sizeof(uint32_t));
++#if FF_API_AVPICTURE
++FF_DISABLE_DEPRECATION_WARNINGS
++        rect = sub->rects[i];
++        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
      }
      return 1;
  }
Simple merge
@@@ -28,9 -28,9 +28,9 @@@
  
  #include "libavutil/version.h"
  
 -#define LIBAVCODEC_VERSION_MAJOR 57
 -#define LIBAVCODEC_VERSION_MINOR  5
 -#define LIBAVCODEC_VERSION_MICRO  0
 +#define LIBAVCODEC_VERSION_MAJOR  57
- #define LIBAVCODEC_VERSION_MINOR   8
++#define LIBAVCODEC_VERSION_MINOR   9
 +#define LIBAVCODEC_VERSION_MICRO 100
  
  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                 LIBAVCODEC_VERSION_MINOR, \
@@@ -57,10 -57,14 +57,12 @@@ static int decode_frame(AVCodecContext 
      int64_t packet_time = 0;
      GetBitContext gb;
      int has_alpha = avctx->codec_tag == MKTAG('D','X','S','A');
+     AVSubtitleRect *rect;
+     int j;
  
 -    memset(sub, 0, sizeof(*sub));
 -
      // check that at least header fits
      if (buf_size < 27 + 7 * 2 + 4 * (3 + has_alpha)) {
 -        av_log(avctx, AV_LOG_ERROR, "coded frame too small\n");
 +        av_log(avctx, AV_LOG_ERROR, "coded frame size %d too small\n", buf_size);
          return -1;
      }
  
@@@ -128,11 -128,24 +128,24 @@@ static int xsub_encode(AVCodecContext *
      }
  
      // TODO: support multiple rects
 -    if (h->num_rects > 1)
 +    if (h->num_rects != 1)
          av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
  
+ #if FF_API_AVPICTURE
+ FF_DISABLE_DEPRECATION_WARNINGS
+     if (!h->rects[0]->data[0]) {
+         AVSubtitleRect *rect = h->rects[0];
+         int j;
+         for (j = 0; j < 4; j++) {
+             rect->data[j] = rect->pict.data[j];
+             rect->linesize[j] = rect->pict.linesize[j];
+         }
+     }
+ FF_ENABLE_DEPRECATION_WARNINGS
+ #endif
      // TODO: render text-based subtitles into bitmaps
-     if (!h->rects[0]->pict.data[0] || !h->rects[0]->pict.data[1]) {
+     if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
          av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
          return -1;
      }
          av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
  
      // TODO: Palette swapping if color zero is not transparent
-     if (((uint32_t *)h->rects[0]->pict.data[1])[0] & 0xff000000)
 -    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff)
++    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
          av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
  
      if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {