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