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