]> git.sesse.net Git - ffmpeg/blob - libavcodec/dvdsub.c
COSMETICS: Remove all trailing whitespace.
[ffmpeg] / libavcodec / dvdsub.c
1 /*
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  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
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 uint16_t getbe16(const uint8_t *p)
32 {
33     return (p[0] << 8) | p[1];
34 }
35
36 int get_nibble(const uint8_t *buf, int nibble_offset)
37 {
38     return (buf[nibble_offset >> 1] >> ((1 - (nibble_offset & 1)) << 2)) & 0xf;
39 }
40
41 static int decode_rle(uint8_t *bitmap, int linesize, int w, int h,
42                       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
95     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
107     if (nb_opaque_colors == 0)
108         return;
109
110     j = nb_opaque_colors;
111     memset(color_used, 0, 16);
112     for(i = 0; i < 4; i++) {
113         if (alpha[i] != 0) {
114             if (!color_used[palette[i]])  {
115                 level = (0xff * j) / nb_opaque_colors;
116                 r = (((subtitle_color >> 16) & 0xff) * level) >> 8;
117                 g = (((subtitle_color >> 8) & 0xff) * level) >> 8;
118                 b = (((subtitle_color >> 0) & 0xff) * level) >> 8;
119                 rgba_palette[i] = b | (g << 8) | (r << 16) | ((alpha[i] * 17) << 24);
120                 color_used[palette[i]] = (i + 1);
121                 j--;
122             } else {
123                 rgba_palette[i] = (rgba_palette[color_used[palette[i]] - 1] & 0x00ffffff) |
124                                     ((alpha[i] * 17) << 24);
125             }
126         }
127     }
128 }
129
130 static int decode_dvd_subtitles(AVSubtitle *sub_header,
131                                 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     int i;
137     int is_menu = 0;
138
139     if (buf_size < 4)
140         return -1;
141     sub_header->rects = NULL;
142     sub_header->num_rects = 0;
143     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         av_log(NULL, AV_LOG_INFO, "cmd_pos=0x%04x next=0x%04x date=%d\n",
152                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                 /* menu subpicture */
166                 is_menu = 1;
167                 break;
168             case 0x01:
169                 /* set start date */
170                 sub_header->start_display_time = (date << 10) / 90;
171                 break;
172             case 0x02:
173                 /* set end date */
174                 sub_header->end_display_time = (date << 10) / 90;
175                 break;
176             case 0x03:
177                 /* set palette */
178                 if ((buf_size - pos) < 2)
179                     goto fail;
180                 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                 pos += 2;
185                 break;
186             case 0x04:
187                 /* set alpha */
188                 if ((buf_size - pos) < 2)
189                     goto fail;
190                 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                 pos += 2;
195 #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                 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
232             /* 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                 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                 bitmap = av_malloc(w * h);
250                 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                 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                 guess_palette(sub_header->rects[0].rgba_palette,
258                               palette, alpha, 0xffff00);
259                 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             }
267         }
268         if (next_cmd_pos == cmd_pos)
269             break;
270         cmd_pos = next_cmd_pos;
271     }
272     if (sub_header->num_rects > 0)
273         return is_menu;
274  fail:
275     return -1;
276 }
277
278 static int is_transp(const uint8_t *buf, int pitch, int n,
279                      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 static int find_smallest_bounding_rectangle(AVSubtitle *s)
292 {
293     uint8_t transp_color[256];
294     int y1, y2, x1, x2, y, w, h, i;
295     uint8_t *bitmap;
296
297     if (s->num_rects == 0 || s->rects == NULL || s->rects[0].w <= 0 || s->rects[0].h <= 0)
298         return 0;
299
300     memset(transp_color, 0, 256);
301     for(i = 0; i < s->rects[0].nb_colors; i++) {
302         if ((s->rects[0].rgba_palette[i] >> 24) == 0)
303             transp_color[i] = 1;
304     }
305     y1 = 0;
306     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         y1++;
309     if (y1 == s->rects[0].h) {
310         av_freep(&s->rects[0].bitmap);
311         s->rects[0].w = s->rects[0].h = 0;
312         return 0;
313     }
314
315     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         y2--;
319     x1 = 0;
320     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         x1++;
323     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                                   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         memcpy(bitmap + w * y, s->rects[0].bitmap + x1 + (y1 + y) * s->rects[0].linesize, w);
334     }
335     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     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     int is_menu;
385
386     is_menu = decode_dvd_subtitles(sub, buf, buf_size);
387
388     if (is_menu < 0) {
389     no_subtitle:
390         *data_size = 0;
391
392         return buf_size;
393     }
394     if (!is_menu && find_smallest_bounding_rectangle(sub) == 0)
395         goto no_subtitle;
396
397 #if defined(DEBUG)
398     av_log(NULL, AV_LOG_INFO, "start=%d ms end =%d ms\n",
399            sub->start_display_time,
400            sub->end_display_time);
401     ppm_save("/tmp/a.ppm", sub->rects[0].bitmap,
402              sub->rects[0].w, sub->rects[0].h, sub->rects[0].rgba_palette);
403 #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                         uint8_t **poutbuf, int *poutbuf_size,
435                         const uint8_t *buf, int buf_size)
436 {
437     DVDSubParseContext *pc = s->priv_data;
438
439     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 };