Statistics
| Branch: | Revision:

ffmpeg / libavcodec / dvdsub.c @ d2d230a7

History | View | Annotate | Download (13.9 KB)

1 240c1657 Fabrice Bellard
/*
2
 * DVD subtitle decoding for ffmpeg
3
 * Copyright (c) 2005 Fabrice Bellard.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17 5509bffa Diego Biurrun
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 240c1657 Fabrice Bellard
 */
19
#include "avcodec.h"
20
21
//#define DEBUG
22
23
typedef struct DVDSubContext {
24
} DVDSubContext;
25
26
static int dvdsub_init_decoder(AVCodecContext *avctx)
27
{
28
    return 0;
29
}
30
31 ceaf1909 Dieter
static uint16_t getbe16(const uint8_t *p)
32 240c1657 Fabrice Bellard
{
33
    return (p[0] << 8) | p[1];
34
}
35
36 ceaf1909 Dieter
static int get_nibble(const uint8_t *buf, int nibble_offset)
37 240c1657 Fabrice Bellard
{
38
    return (buf[nibble_offset >> 1] >> ((1 - (nibble_offset & 1)) << 2)) & 0xf;
39
}
40
41 115329f1 Diego Biurrun
static int decode_rle(uint8_t *bitmap, int linesize, int w, int h,
42 240c1657 Fabrice Bellard
                      const uint8_t *buf, int nibble_offset, int buf_size)
43
{
44
    unsigned int v;
45
    int x, y, len, color, nibble_end;
46
    uint8_t *d;
47
48
    nibble_end = buf_size * 2;
49
    x = 0;
50
    y = 0;
51
    d = bitmap;
52
    for(;;) {
53
        if (nibble_offset >= nibble_end)
54
            return -1;
55
        v = get_nibble(buf, nibble_offset++);
56
        if (v < 0x4) {
57
            v = (v << 4) | get_nibble(buf, nibble_offset++);
58
            if (v < 0x10) {
59
                v = (v << 4) | get_nibble(buf, nibble_offset++);
60
                if (v < 0x040) {
61
                    v = (v << 4) | get_nibble(buf, nibble_offset++);
62
                    if (v < 4) {
63
                        v |= (w - x) << 2;
64
                    }
65
                }
66
            }
67
        }
68
        len = v >> 2;
69
        if (len > (w - x))
70
            len = (w - x);
71
        color = v & 0x03;
72
        memset(d + x, color, len);
73
        x += len;
74
        if (x >= w) {
75
            y++;
76
            if (y >= h)
77
                break;
78
            d += linesize;
79
            x = 0;
80
            /* byte align */
81
            nibble_offset += (nibble_offset & 1);
82
        }
83
    }
84
    return 0;
85
}
86
87
static void guess_palette(uint32_t *rgba_palette,
88
                          uint8_t *palette,
89
                          uint8_t *alpha,
90
                          uint32_t subtitle_color)
91
{
92
    uint8_t color_used[16];
93
    int nb_opaque_colors, i, level, j, r, g, b;
94 115329f1 Diego Biurrun
95 240c1657 Fabrice Bellard
    for(i = 0; i < 4; i++)
96
        rgba_palette[i] = 0;
97
98
    memset(color_used, 0, 16);
99
    nb_opaque_colors = 0;
100
    for(i = 0; i < 4; i++) {
101
        if (alpha[i] != 0 && !color_used[palette[i]]) {
102
            color_used[palette[i]] = 1;
103
            nb_opaque_colors++;
104
        }
105
    }
106 115329f1 Diego Biurrun
107 240c1657 Fabrice Bellard
    if (nb_opaque_colors == 0)
108
        return;
109 115329f1 Diego Biurrun
110 bf01fb69 Ian Caulfield
    j = nb_opaque_colors;
111 240c1657 Fabrice Bellard
    memset(color_used, 0, 16);
112
    for(i = 0; i < 4; i++) {
113
        if (alpha[i] != 0) {
114
            if (!color_used[palette[i]])  {
115 bf01fb69 Ian Caulfield
                level = (0xff * j) / nb_opaque_colors;
116 240c1657 Fabrice Bellard
                r = (((subtitle_color >> 16) & 0xff) * level) >> 8;
117
                g = (((subtitle_color >> 8) & 0xff) * level) >> 8;
118
                b = (((subtitle_color >> 0) & 0xff) * level) >> 8;
119 bf01fb69 Ian Caulfield
                rgba_palette[i] = b | (g << 8) | (r << 16) | ((alpha[i] * 17) << 24);
120 240c1657 Fabrice Bellard
                color_used[palette[i]] = (i + 1);
121 bf01fb69 Ian Caulfield
                j--;
122 240c1657 Fabrice Bellard
            } else {
123 bf01fb69 Ian Caulfield
                rgba_palette[i] = (rgba_palette[color_used[palette[i]] - 1] & 0x00ffffff) |
124
                                    ((alpha[i] * 17) << 24);
125 240c1657 Fabrice Bellard
            }
126
        }
127
    }
128
}
129
130 115329f1 Diego Biurrun
static int decode_dvd_subtitles(AVSubtitle *sub_header,
131 240c1657 Fabrice Bellard
                                const uint8_t *buf, int buf_size)
132
{
133
    int cmd_pos, pos, cmd, x1, y1, x2, y2, offset1, offset2, next_cmd_pos;
134
    uint8_t palette[4], alpha[4];
135
    int date;
136 50e2450b Michael Niedermayer
    int i;
137 bf01fb69 Ian Caulfield
    int is_menu = 0;
138 115329f1 Diego Biurrun
139 240c1657 Fabrice Bellard
    if (buf_size < 4)
140
        return -1;
141 50e2450b Michael Niedermayer
    sub_header->rects = NULL;
142
    sub_header->num_rects = 0;
143 240c1657 Fabrice Bellard
    sub_header->start_display_time = 0;
144
    sub_header->end_display_time = 0;
145
146
    cmd_pos = getbe16(buf + 2);
147
    while ((cmd_pos + 4) < buf_size) {
148
        date = getbe16(buf + cmd_pos);
149
        next_cmd_pos = getbe16(buf + cmd_pos + 2);
150
#ifdef DEBUG
151 115329f1 Diego Biurrun
        av_log(NULL, AV_LOG_INFO, "cmd_pos=0x%04x next=0x%04x date=%d\n",
152 240c1657 Fabrice Bellard
               cmd_pos, next_cmd_pos, date);
153
#endif
154
        pos = cmd_pos + 4;
155
        offset1 = -1;
156
        offset2 = -1;
157
        x1 = y1 = x2 = y2 = 0;
158
        while (pos < buf_size) {
159
            cmd = buf[pos++];
160
#ifdef DEBUG
161
            av_log(NULL, AV_LOG_INFO, "cmd=%02x\n", cmd);
162
#endif
163
            switch(cmd) {
164
            case 0x00:
165 bf01fb69 Ian Caulfield
                /* menu subpicture */
166
                is_menu = 1;
167 240c1657 Fabrice Bellard
                break;
168
            case 0x01:
169
                /* set start date */
170 bf01fb69 Ian Caulfield
                sub_header->start_display_time = (date << 10) / 90;
171 240c1657 Fabrice Bellard
                break;
172
            case 0x02:
173
                /* set end date */
174 bf01fb69 Ian Caulfield
                sub_header->end_display_time = (date << 10) / 90;
175 240c1657 Fabrice Bellard
                break;
176
            case 0x03:
177
                /* set palette */
178
                if ((buf_size - pos) < 2)
179
                    goto fail;
180 bf01fb69 Ian Caulfield
                palette[3] = buf[pos] >> 4;
181
                palette[2] = buf[pos] & 0x0f;
182
                palette[1] = buf[pos + 1] >> 4;
183
                palette[0] = buf[pos + 1] & 0x0f;
184 240c1657 Fabrice Bellard
                pos += 2;
185
                break;
186
            case 0x04:
187
                /* set alpha */
188
                if ((buf_size - pos) < 2)
189
                    goto fail;
190 bf01fb69 Ian Caulfield
                alpha[3] = buf[pos] >> 4;
191
                alpha[2] = buf[pos] & 0x0f;
192
                alpha[1] = buf[pos + 1] >> 4;
193
                alpha[0] = buf[pos + 1] & 0x0f;
194 240c1657 Fabrice Bellard
                pos += 2;
195 bf01fb69 Ian Caulfield
#ifdef DEBUG
196
            av_log(NULL, AV_LOG_INFO, "alpha=%x%x%x%x\n", alpha[0],alpha[1],alpha[2],alpha[3]);
197
#endif
198 240c1657 Fabrice Bellard
                break;
199
            case 0x05:
200
                if ((buf_size - pos) < 6)
201
                    goto fail;
202
                x1 = (buf[pos] << 4) | (buf[pos + 1] >> 4);
203
                x2 = ((buf[pos + 1] & 0x0f) << 8) | buf[pos + 2];
204
                y1 = (buf[pos + 3] << 4) | (buf[pos + 4] >> 4);
205
                y2 = ((buf[pos + 4] & 0x0f) << 8) | buf[pos + 5];
206
#ifdef DEBUG
207
                av_log(NULL, AV_LOG_INFO, "x1=%d x2=%d y1=%d y2=%d\n",
208
                       x1, x2, y1, y2);
209
#endif
210
                pos += 6;
211
                break;
212
            case 0x06:
213
                if ((buf_size - pos) < 4)
214
                    goto fail;
215
                offset1 = getbe16(buf + pos);
216
                offset2 = getbe16(buf + pos + 2);
217
#ifdef DEBUG
218
                av_log(NULL, AV_LOG_INFO, "offset1=0x%04x offset2=0x%04x\n", offset1, offset2);
219
#endif
220
                pos += 4;
221
                break;
222
            case 0xff:
223
            default:
224
                goto the_end;
225
            }
226
        }
227
    the_end:
228
        if (offset1 >= 0) {
229
            int w, h;
230
            uint8_t *bitmap;
231 115329f1 Diego Biurrun
232 240c1657 Fabrice Bellard
            /* decode the bitmap */
233
            w = x2 - x1 + 1;
234
            if (w < 0)
235
                w = 0;
236
            h = y2 - y1;
237
            if (h < 0)
238
                h = 0;
239
            if (w > 0 && h > 0) {
240 50e2450b Michael Niedermayer
                if (sub_header->rects != NULL) {
241
                    for (i = 0; i < sub_header->num_rects; i++) {
242
                        av_free(sub_header->rects[i].bitmap);
243
                        av_free(sub_header->rects[i].rgba_palette);
244
                    }
245
                    av_freep(&sub_header->rects);
246
                    sub_header->num_rects = 0;
247
                }
248
249 240c1657 Fabrice Bellard
                bitmap = av_malloc(w * h);
250 50e2450b Michael Niedermayer
                sub_header->rects = av_mallocz(sizeof(AVSubtitleRect));
251
                sub_header->num_rects = 1;
252
                sub_header->rects[0].rgba_palette = av_malloc(4 * 4);
253 240c1657 Fabrice Bellard
                decode_rle(bitmap, w * 2, w, h / 2,
254
                           buf, offset1 * 2, buf_size);
255
                decode_rle(bitmap + w, w * 2, w, h / 2,
256
                           buf, offset2 * 2, buf_size);
257 50e2450b Michael Niedermayer
                guess_palette(sub_header->rects[0].rgba_palette,
258 240c1657 Fabrice Bellard
                              palette, alpha, 0xffff00);
259 50e2450b Michael Niedermayer
                sub_header->rects[0].x = x1;
260
                sub_header->rects[0].y = y1;
261
                sub_header->rects[0].w = w;
262
                sub_header->rects[0].h = h;
263
                sub_header->rects[0].nb_colors = 4;
264
                sub_header->rects[0].linesize = w;
265
                sub_header->rects[0].bitmap = bitmap;
266 240c1657 Fabrice Bellard
            }
267
        }
268
        if (next_cmd_pos == cmd_pos)
269
            break;
270
        cmd_pos = next_cmd_pos;
271
    }
272 50e2450b Michael Niedermayer
    if (sub_header->num_rects > 0)
273 bf01fb69 Ian Caulfield
        return is_menu;
274 240c1657 Fabrice Bellard
 fail:
275
    return -1;
276
}
277
278 115329f1 Diego Biurrun
static int is_transp(const uint8_t *buf, int pitch, int n,
279 240c1657 Fabrice Bellard
                     const uint8_t *transp_color)
280
{
281
    int i;
282
    for(i = 0; i < n; i++) {
283
        if (!transp_color[*buf])
284
            return 0;
285
        buf += pitch;
286
    }
287
    return 1;
288
}
289
290
/* return 0 if empty rectangle, 1 if non empty */
291 bf01fb69 Ian Caulfield
static int find_smallest_bounding_rectangle(AVSubtitle *s)
292 240c1657 Fabrice Bellard
{
293
    uint8_t transp_color[256];
294
    int y1, y2, x1, x2, y, w, h, i;
295
    uint8_t *bitmap;
296
297 50e2450b Michael Niedermayer
    if (s->num_rects == 0 || s->rects == NULL || s->rects[0].w <= 0 || s->rects[0].h <= 0)
298 240c1657 Fabrice Bellard
        return 0;
299
300
    memset(transp_color, 0, 256);
301 50e2450b Michael Niedermayer
    for(i = 0; i < s->rects[0].nb_colors; i++) {
302
        if ((s->rects[0].rgba_palette[i] >> 24) == 0)
303 240c1657 Fabrice Bellard
            transp_color[i] = 1;
304
    }
305
    y1 = 0;
306 50e2450b Michael Niedermayer
    while (y1 < s->rects[0].h && is_transp(s->rects[0].bitmap + y1 * s->rects[0].linesize,
307
                                  1, s->rects[0].w, transp_color))
308 240c1657 Fabrice Bellard
        y1++;
309 50e2450b Michael Niedermayer
    if (y1 == s->rects[0].h) {
310
        av_freep(&s->rects[0].bitmap);
311
        s->rects[0].w = s->rects[0].h = 0;
312 240c1657 Fabrice Bellard
        return 0;
313
    }
314
315 50e2450b Michael Niedermayer
    y2 = s->rects[0].h - 1;
316
    while (y2 > 0 && is_transp(s->rects[0].bitmap + y2 * s->rects[0].linesize, 1,
317
                               s->rects[0].w, transp_color))
318 240c1657 Fabrice Bellard
        y2--;
319
    x1 = 0;
320 50e2450b Michael Niedermayer
    while (x1 < (s->rects[0].w - 1) && is_transp(s->rects[0].bitmap + x1, s->rects[0].linesize,
321
                                        s->rects[0].h, transp_color))
322 240c1657 Fabrice Bellard
        x1++;
323 50e2450b Michael Niedermayer
    x2 = s->rects[0].w - 1;
324
    while (x2 > 0 && is_transp(s->rects[0].bitmap + x2, s->rects[0].linesize, s->rects[0].h,
325 240c1657 Fabrice Bellard
                                  transp_color))
326
        x2--;
327
    w = x2 - x1 + 1;
328
    h = y2 - y1 + 1;
329
    bitmap = av_malloc(w * h);
330
    if (!bitmap)
331
        return 1;
332
    for(y = 0; y < h; y++) {
333 50e2450b Michael Niedermayer
        memcpy(bitmap + w * y, s->rects[0].bitmap + x1 + (y1 + y) * s->rects[0].linesize, w);
334 240c1657 Fabrice Bellard
    }
335 50e2450b Michael Niedermayer
    av_freep(&s->rects[0].bitmap);
336
    s->rects[0].bitmap = bitmap;
337
    s->rects[0].linesize = w;
338
    s->rects[0].w = w;
339
    s->rects[0].h = h;
340
    s->rects[0].x += x1;
341
    s->rects[0].y += y1;
342 240c1657 Fabrice Bellard
    return 1;
343
}
344
345
static int dvdsub_close_decoder(AVCodecContext *avctx)
346
{
347
    return 0;
348
}
349
350
#ifdef DEBUG
351
#undef fprintf
352
static void ppm_save(const char *filename, uint8_t *bitmap, int w, int h,
353
                     uint32_t *rgba_palette)
354
{
355
    int x, y, v;
356
    FILE *f;
357
358
    f = fopen(filename, "w");
359
    if (!f) {
360
        perror(filename);
361
        exit(1);
362
    }
363
    fprintf(f, "P6\n"
364
            "%d %d\n"
365
            "%d\n",
366
            w, h, 255);
367
    for(y = 0; y < h; y++) {
368
        for(x = 0; x < w; x++) {
369
            v = rgba_palette[bitmap[y * w + x]];
370
            putc((v >> 16) & 0xff, f);
371
            putc((v >> 8) & 0xff, f);
372
            putc((v >> 0) & 0xff, f);
373
        }
374
    }
375
    fclose(f);
376
}
377
#endif
378
379
static int dvdsub_decode(AVCodecContext *avctx,
380
                         void *data, int *data_size,
381
                         uint8_t *buf, int buf_size)
382
{
383
    AVSubtitle *sub = (void *)data;
384 bf01fb69 Ian Caulfield
    int is_menu;
385
386
    is_menu = decode_dvd_subtitles(sub, buf, buf_size);
387 240c1657 Fabrice Bellard
388 bf01fb69 Ian Caulfield
    if (is_menu < 0) {
389 240c1657 Fabrice Bellard
    no_subtitle:
390
        *data_size = 0;
391
392
        return buf_size;
393
    }
394 bf01fb69 Ian Caulfield
    if (!is_menu && find_smallest_bounding_rectangle(sub) == 0)
395 240c1657 Fabrice Bellard
        goto no_subtitle;
396
397
#if defined(DEBUG)
398 115329f1 Diego Biurrun
    av_log(NULL, AV_LOG_INFO, "start=%d ms end =%d ms\n",
399 240c1657 Fabrice Bellard
           sub->start_display_time,
400
           sub->end_display_time);
401 50e2450b Michael Niedermayer
    ppm_save("/tmp/a.ppm", sub->rects[0].bitmap,
402
             sub->rects[0].w, sub->rects[0].h, sub->rects[0].rgba_palette);
403 240c1657 Fabrice Bellard
#endif
404
405
    *data_size = 1;
406
    return buf_size;
407
}
408
409
AVCodec dvdsub_decoder = {
410
    "dvdsub",
411
    CODEC_TYPE_SUBTITLE,
412
    CODEC_ID_DVD_SUBTITLE,
413
    sizeof(DVDSubContext),
414
    dvdsub_init_decoder,
415
    NULL,
416
    dvdsub_close_decoder,
417
    dvdsub_decode,
418
};
419
420
/* parser definition */
421
typedef struct DVDSubParseContext {
422
    uint8_t *packet;
423
    int packet_len;
424
    int packet_index;
425
} DVDSubParseContext;
426
427
static int dvdsub_parse_init(AVCodecParserContext *s)
428
{
429
    return 0;
430
}
431
432
static int dvdsub_parse(AVCodecParserContext *s,
433
                        AVCodecContext *avctx,
434 115329f1 Diego Biurrun
                        uint8_t **poutbuf, int *poutbuf_size,
435 240c1657 Fabrice Bellard
                        const uint8_t *buf, int buf_size)
436
{
437
    DVDSubParseContext *pc = s->priv_data;
438 115329f1 Diego Biurrun
439 240c1657 Fabrice Bellard
    if (pc->packet_index == 0) {
440
        if (buf_size < 2)
441
            return 0;
442
        pc->packet_len = (buf[0] << 8) | buf[1];
443
        av_freep(&pc->packet);
444
        pc->packet = av_malloc(pc->packet_len);
445
    }
446
    if (pc->packet) {
447
        if (pc->packet_index + buf_size <= pc->packet_len) {
448
            memcpy(pc->packet + pc->packet_index, buf, buf_size);
449
            pc->packet_index += buf_size;
450
            if (pc->packet_index >= pc->packet_len) {
451
                *poutbuf = pc->packet;
452
                *poutbuf_size = pc->packet_len;
453
                pc->packet_index = 0;
454
                return buf_size;
455
            }
456
        } else {
457
            /* erroneous size */
458
            pc->packet_index = 0;
459
        }
460
    }
461
    *poutbuf = NULL;
462
    *poutbuf_size = 0;
463
    return buf_size;
464
}
465
466
static void dvdsub_parse_close(AVCodecParserContext *s)
467
{
468
    DVDSubParseContext *pc = s->priv_data;
469
    av_freep(&pc->packet);
470
}
471
472
AVCodecParser dvdsub_parser = {
473
    { CODEC_ID_DVD_SUBTITLE },
474
    sizeof(DVDSubParseContext),
475
    dvdsub_parse_init,
476
    dvdsub_parse,
477
    dvdsub_parse_close,
478
};