dvdsubdec.c 15.1 KB
Newer Older
1 2 3 4
/*
 * DVD subtitle decoding for ffmpeg
 * Copyright (c) 2005 Fabrice Bellard.
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13 14 15 16 17
 * 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
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 21
 */
#include "avcodec.h"
22 23 24
#include "bitstream.h"
#include "colorspace.h"
#include "dsputil.h"
25 26 27

//#define DEBUG

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
static void yuv_a_to_rgba(const uint8_t *ycbcr, const uint8_t *alpha, uint32_t *rgba, int num_values)
{
    uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
    uint8_t r, g, b;
    int i, y, cb, cr;
    int r_add, g_add, b_add;

    for (i = num_values; i > 0; i--) {
        y = *ycbcr++;
        cb = *ycbcr++;
        cr = *ycbcr++;
        YUV_TO_RGB1_CCIR(cb, cr);
        YUV_TO_RGB2_CCIR(r, g, b, y);
        *rgba++ = (*alpha++ << 24) | (r << 16) | (g << 8) | b;
    }
}

static int decode_run_2bit(GetBitContext *gb, int *color)
{
    unsigned int v, t;

    v = 0;
    for (t = 1; v < t && t <= 0x40; t <<= 2)
        v = (v << 4) | get_bits(gb, 4);
    *color = v & 3;
    if (v < 4) { /* Code for fill rest of line */
        return INT_MAX;
    }
    return v >> 2;
}

static int decode_run_8bit(GetBitContext *gb, int *color)
60
{
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
    int len;
    int has_run = get_bits1(gb);
    if (get_bits1(gb))
        *color = get_bits(gb, 8);
    else
        *color = get_bits(gb, 2);
    if (has_run) {
        if (get_bits1(gb)) {
            len = get_bits(gb, 7);
            if (len == 0)
                len = INT_MAX;
            else
                len += 9;
        } else
            len = get_bits(gb, 3) + 2;
    } else
        len = 1;
    return len;
79 80
}

81
static int decode_rle(uint8_t *bitmap, int linesize, int w, int h,
82
                      const uint8_t *buf, int start, int buf_size, int is_8bit)
83
{
84 85 86
    GetBitContext gb;
    int bit_len;
    int x, y, len, color;
87 88
    uint8_t *d;

89 90 91
    bit_len = (buf_size - start) * 8;
    init_get_bits(&gb, buf + start, bit_len);

92 93 94 95
    x = 0;
    y = 0;
    d = bitmap;
    for(;;) {
96
        if (get_bits_count(&gb) > bit_len)
97
            return -1;
98 99 100 101 102
        if (is_8bit)
            len = decode_run_8bit(&gb, &color);
        else
            len = decode_run_2bit(&gb, &color);
        len = FFMIN(len, w - x);
103 104 105 106 107 108 109 110 111
        memset(d + x, color, len);
        x += len;
        if (x >= w) {
            y++;
            if (y >= h)
                break;
            d += linesize;
            x = 0;
            /* byte align */
112
            align_get_bits(&gb);
113 114 115 116 117 118
        }
    }
    return 0;
}

static void guess_palette(uint32_t *rgba_palette,
119
                          uint8_t *colormap,
120 121 122 123 124
                          uint8_t *alpha,
                          uint32_t subtitle_color)
{
    uint8_t color_used[16];
    int nb_opaque_colors, i, level, j, r, g, b;
125

126 127 128 129 130 131
    for(i = 0; i < 4; i++)
        rgba_palette[i] = 0;

    memset(color_used, 0, 16);
    nb_opaque_colors = 0;
    for(i = 0; i < 4; i++) {
132 133
        if (alpha[i] != 0 && !color_used[colormap[i]]) {
            color_used[colormap[i]] = 1;
134 135 136
            nb_opaque_colors++;
        }
    }
137

138 139
    if (nb_opaque_colors == 0)
        return;
140

141
    j = nb_opaque_colors;
142 143 144
    memset(color_used, 0, 16);
    for(i = 0; i < 4; i++) {
        if (alpha[i] != 0) {
145
            if (!color_used[colormap[i]])  {
146
                level = (0xff * j) / nb_opaque_colors;
147 148 149
                r = (((subtitle_color >> 16) & 0xff) * level) >> 8;
                g = (((subtitle_color >> 8) & 0xff) * level) >> 8;
                b = (((subtitle_color >> 0) & 0xff) * level) >> 8;
150
                rgba_palette[i] = b | (g << 8) | (r << 16) | ((alpha[i] * 17) << 24);
151
                color_used[colormap[i]] = (i + 1);
152
                j--;
153
            } else {
154
                rgba_palette[i] = (rgba_palette[color_used[colormap[i]] - 1] & 0x00ffffff) |
155
                                    ((alpha[i] * 17) << 24);
156 157 158 159 160
            }
        }
    }
}

161 162
#define READ_OFFSET(a) (big_offsets ? AV_RB32(a) : AV_RB16(a))

163
static int decode_dvd_subtitles(AVSubtitle *sub_header,
164 165 166
                                const uint8_t *buf, int buf_size)
{
    int cmd_pos, pos, cmd, x1, y1, x2, y2, offset1, offset2, next_cmd_pos;
167 168 169
    int big_offsets, offset_size, is_8bit = 0;
    const uint8_t *yuv_palette = 0;
    uint8_t colormap[4], alpha[256];
170
    int date;
171
    int i;
172
    int is_menu = 0;
173

174
    if (buf_size < 10)
175
        return -1;
176 177
    sub_header->rects = NULL;
    sub_header->num_rects = 0;
178 179 180
    sub_header->start_display_time = 0;
    sub_header->end_display_time = 0;

181 182 183 184 185 186 187 188 189 190 191 192 193
    if (AV_RB16(buf) == 0) {   /* HD subpicture with 4-byte offsets */
        big_offsets = 1;
        offset_size = 4;
        cmd_pos = 6;
    } else {
        big_offsets = 0;
        offset_size = 2;
        cmd_pos = 2;
    }

    cmd_pos = READ_OFFSET(buf + cmd_pos);

    while ((cmd_pos + 2 + offset_size) < buf_size) {
194
        date = AV_RB16(buf + cmd_pos);
195
        next_cmd_pos = READ_OFFSET(buf + cmd_pos + 2);
196
#ifdef DEBUG
197
        av_log(NULL, AV_LOG_INFO, "cmd_pos=0x%04x next=0x%04x date=%d\n",
198 199
               cmd_pos, next_cmd_pos, date);
#endif
200
        pos = cmd_pos + 2 + offset_size;
201 202 203 204 205 206 207 208 209 210
        offset1 = -1;
        offset2 = -1;
        x1 = y1 = x2 = y2 = 0;
        while (pos < buf_size) {
            cmd = buf[pos++];
#ifdef DEBUG
            av_log(NULL, AV_LOG_INFO, "cmd=%02x\n", cmd);
#endif
            switch(cmd) {
            case 0x00:
211 212
                /* menu subpicture */
                is_menu = 1;
213 214 215
                break;
            case 0x01:
                /* set start date */
216
                sub_header->start_display_time = (date << 10) / 90;
217 218 219
                break;
            case 0x02:
                /* set end date */
220
                sub_header->end_display_time = (date << 10) / 90;
221 222
                break;
            case 0x03:
223
                /* set colormap */
224 225
                if ((buf_size - pos) < 2)
                    goto fail;
226 227 228 229
                colormap[3] = buf[pos] >> 4;
                colormap[2] = buf[pos] & 0x0f;
                colormap[1] = buf[pos + 1] >> 4;
                colormap[0] = buf[pos + 1] & 0x0f;
230 231 232 233 234 235
                pos += 2;
                break;
            case 0x04:
                /* set alpha */
                if ((buf_size - pos) < 2)
                    goto fail;
236 237 238 239
                alpha[3] = buf[pos] >> 4;
                alpha[2] = buf[pos] & 0x0f;
                alpha[1] = buf[pos + 1] >> 4;
                alpha[0] = buf[pos + 1] & 0x0f;
240
                pos += 2;
241 242 243
#ifdef DEBUG
            av_log(NULL, AV_LOG_INFO, "alpha=%x%x%x%x\n", alpha[0],alpha[1],alpha[2],alpha[3]);
#endif
244 245
                break;
            case 0x05:
246
            case 0x85:
247 248 249 250 251 252
                if ((buf_size - pos) < 6)
                    goto fail;
                x1 = (buf[pos] << 4) | (buf[pos + 1] >> 4);
                x2 = ((buf[pos + 1] & 0x0f) << 8) | buf[pos + 2];
                y1 = (buf[pos + 3] << 4) | (buf[pos + 4] >> 4);
                y2 = ((buf[pos + 4] & 0x0f) << 8) | buf[pos + 5];
253 254
                if (cmd & 0x80)
                    is_8bit = 1;
255 256 257 258 259 260 261 262 263
#ifdef DEBUG
                av_log(NULL, AV_LOG_INFO, "x1=%d x2=%d y1=%d y2=%d\n",
                       x1, x2, y1, y2);
#endif
                pos += 6;
                break;
            case 0x06:
                if ((buf_size - pos) < 4)
                    goto fail;
264 265
                offset1 = AV_RB16(buf + pos);
                offset2 = AV_RB16(buf + pos + 2);
266 267 268 269 270
#ifdef DEBUG
                av_log(NULL, AV_LOG_INFO, "offset1=0x%04x offset2=0x%04x\n", offset1, offset2);
#endif
                pos += 4;
                break;
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
            case 0x86:
                if ((buf_size - pos) < 8)
                    goto fail;
                offset1 = AV_RB32(buf + pos);
                offset2 = AV_RB32(buf + pos + 4);
#ifdef DEBUG
                av_log(NULL, AV_LOG_INFO, "offset1=0x%04x offset2=0x%04x\n", offset1, offset2);
#endif
                pos += 8;
                break;

            case 0x83:
                /* HD set palette */
                if ((buf_size - pos) < 768)
                    goto fail;
                yuv_palette = buf + pos;
                pos += 768;
                break;
            case 0x84:
                /* HD set contrast (alpha) */
                if ((buf_size - pos) < 256)
                    goto fail;
                for (i = 0; i < 256; i++)
                    alpha[i] = 0xFF - buf[pos+i];
                pos += 256;
                break;

298
            case 0xff:
299
                goto the_end;
300
            default:
301 302 303
#ifdef DEBUG
                av_log(NULL, AV_LOG_INFO, "unrecognised subpicture command 0x%x\n", cmd);
#endif
304 305 306 307 308 309 310
                goto the_end;
            }
        }
    the_end:
        if (offset1 >= 0) {
            int w, h;
            uint8_t *bitmap;
311

312 313 314 315 316 317 318 319
            /* decode the bitmap */
            w = x2 - x1 + 1;
            if (w < 0)
                w = 0;
            h = y2 - y1;
            if (h < 0)
                h = 0;
            if (w > 0 && h > 0) {
320 321 322 323 324 325 326 327 328
                if (sub_header->rects != NULL) {
                    for (i = 0; i < sub_header->num_rects; i++) {
                        av_free(sub_header->rects[i].bitmap);
                        av_free(sub_header->rects[i].rgba_palette);
                    }
                    av_freep(&sub_header->rects);
                    sub_header->num_rects = 0;
                }

329
                bitmap = av_malloc(w * h);
330 331
                sub_header->rects = av_mallocz(sizeof(AVSubtitleRect));
                sub_header->num_rects = 1;
332
                sub_header->rects[0].bitmap = bitmap;
333
                decode_rle(bitmap, w * 2, w, (h + 1) / 2,
334
                           buf, offset1, buf_size, is_8bit);
335
                decode_rle(bitmap + w, w * 2, w, h / 2,
336 337 338 339 340 341 342 343 344 345
                           buf, offset2, buf_size, is_8bit);
                if (is_8bit) {
                    if (yuv_palette == 0)
                        goto fail;
                    sub_header->rects[0].rgba_palette = av_malloc(256 * 4);
                    sub_header->rects[0].nb_colors = 256;
                    yuv_a_to_rgba(yuv_palette, alpha, sub_header->rects[0].rgba_palette, 256);
                } else {
                    sub_header->rects[0].rgba_palette = av_malloc(4 * 4);
                    sub_header->rects[0].nb_colors = 4;
Ian Caulfield's avatar
Ian Caulfield committed
346 347
                    guess_palette(sub_header->rects[0].rgba_palette,
                                  colormap, alpha, 0xffff00);
348
                }
349 350 351 352 353
                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].linesize = w;
354 355 356 357 358 359
            }
        }
        if (next_cmd_pos == cmd_pos)
            break;
        cmd_pos = next_cmd_pos;
    }
360
    if (sub_header->num_rects > 0)
361
        return is_menu;
362
 fail:
363 364 365 366 367 368 369 370
    if (sub_header->rects != NULL) {
        for (i = 0; i < sub_header->num_rects; i++) {
            av_free(sub_header->rects[i].bitmap);
            av_free(sub_header->rects[i].rgba_palette);
        }
        av_freep(&sub_header->rects);
        sub_header->num_rects = 0;
    }
371 372 373
    return -1;
}

374
static int is_transp(const uint8_t *buf, int pitch, int n,
375 376 377 378 379 380 381 382 383 384 385 386
                     const uint8_t *transp_color)
{
    int i;
    for(i = 0; i < n; i++) {
        if (!transp_color[*buf])
            return 0;
        buf += pitch;
    }
    return 1;
}

/* return 0 if empty rectangle, 1 if non empty */
387
static int find_smallest_bounding_rectangle(AVSubtitle *s)
388 389 390 391 392
{
    uint8_t transp_color[256];
    int y1, y2, x1, x2, y, w, h, i;
    uint8_t *bitmap;

393
    if (s->num_rects == 0 || s->rects == NULL || s->rects[0].w <= 0 || s->rects[0].h <= 0)
394 395 396
        return 0;

    memset(transp_color, 0, 256);
397 398
    for(i = 0; i < s->rects[0].nb_colors; i++) {
        if ((s->rects[0].rgba_palette[i] >> 24) == 0)
399 400 401
            transp_color[i] = 1;
    }
    y1 = 0;
402 403
    while (y1 < s->rects[0].h && is_transp(s->rects[0].bitmap + y1 * s->rects[0].linesize,
                                  1, s->rects[0].w, transp_color))
404
        y1++;
405 406 407
    if (y1 == s->rects[0].h) {
        av_freep(&s->rects[0].bitmap);
        s->rects[0].w = s->rects[0].h = 0;
408 409 410
        return 0;
    }

411 412 413
    y2 = s->rects[0].h - 1;
    while (y2 > 0 && is_transp(s->rects[0].bitmap + y2 * s->rects[0].linesize, 1,
                               s->rects[0].w, transp_color))
414 415
        y2--;
    x1 = 0;
416 417
    while (x1 < (s->rects[0].w - 1) && is_transp(s->rects[0].bitmap + x1, s->rects[0].linesize,
                                        s->rects[0].h, transp_color))
418
        x1++;
419 420
    x2 = s->rects[0].w - 1;
    while (x2 > 0 && is_transp(s->rects[0].bitmap + x2, s->rects[0].linesize, s->rects[0].h,
421 422 423 424 425 426 427 428
                                  transp_color))
        x2--;
    w = x2 - x1 + 1;
    h = y2 - y1 + 1;
    bitmap = av_malloc(w * h);
    if (!bitmap)
        return 1;
    for(y = 0; y < h; y++) {
429
        memcpy(bitmap + w * y, s->rects[0].bitmap + x1 + (y1 + y) * s->rects[0].linesize, w);
430
    }
431 432 433 434 435 436 437
    av_freep(&s->rects[0].bitmap);
    s->rects[0].bitmap = bitmap;
    s->rects[0].linesize = w;
    s->rects[0].w = w;
    s->rects[0].h = h;
    s->rects[0].x += x1;
    s->rects[0].y += y1;
438 439 440 441 442
    return 1;
}

#ifdef DEBUG
#undef fprintf
443 444
#undef perror
#undef exit
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
static void ppm_save(const char *filename, uint8_t *bitmap, int w, int h,
                     uint32_t *rgba_palette)
{
    int x, y, v;
    FILE *f;

    f = fopen(filename, "w");
    if (!f) {
        perror(filename);
        exit(1);
    }
    fprintf(f, "P6\n"
            "%d %d\n"
            "%d\n",
            w, h, 255);
    for(y = 0; y < h; y++) {
        for(x = 0; x < w; x++) {
            v = rgba_palette[bitmap[y * w + x]];
            putc((v >> 16) & 0xff, f);
            putc((v >> 8) & 0xff, f);
            putc((v >> 0) & 0xff, f);
        }
    }
    fclose(f);
}
#endif

static int dvdsub_decode(AVCodecContext *avctx,
                         void *data, int *data_size,
Michael Niedermayer's avatar
Michael Niedermayer committed
474
                         const uint8_t *buf, int buf_size)
475 476
{
    AVSubtitle *sub = (void *)data;
477 478 479
    int is_menu;

    is_menu = decode_dvd_subtitles(sub, buf, buf_size);
480

481
    if (is_menu < 0) {
482 483 484 485 486
    no_subtitle:
        *data_size = 0;

        return buf_size;
    }
487
    if (!is_menu && find_smallest_bounding_rectangle(sub) == 0)
488 489 490
        goto no_subtitle;

#if defined(DEBUG)
491
    av_log(NULL, AV_LOG_INFO, "start=%d ms end =%d ms\n",
492 493
           sub->start_display_time,
           sub->end_display_time);
494 495
    ppm_save("/tmp/a.ppm", sub->rects[0].bitmap,
             sub->rects[0].w, sub->rects[0].h, sub->rects[0].rgba_palette);
496 497 498 499 500 501 502 503 504 505
#endif

    *data_size = 1;
    return buf_size;
}

AVCodec dvdsub_decoder = {
    "dvdsub",
    CODEC_TYPE_SUBTITLE,
    CODEC_ID_DVD_SUBTITLE,
Steve L'Homme's avatar
Steve L'Homme committed
506
    0,
507
    NULL,
508 509
    NULL,
    NULL,
510
    dvdsub_decode,
511
    .long_name = NULL_IF_CONFIG_SMALL("DVD subtitles"),
512
};