Mercurial > libavcodec.hg
annotate dvdsub.c @ 2918:13dcd22f0816 libavcodec
Add DTS_INC to the CFLAGS for DTS. This is only set by external configure
programs (such as the MPlayer one) and thus somewhat hackish. We already
do this for things like MLIB_INC and IPP_INC so it should be acceptable.
author | diego |
---|---|
date | Sun, 23 Oct 2005 18:16:53 +0000 |
parents | 1f117208d20f |
children | ef2149182f1c |
rev | line source |
---|---|
2756 | 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 | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
110 j = nb_opaque_colors; |
2756 | 111 memset(color_used, 0, 16); |
112 for(i = 0; i < 4; i++) { | |
113 if (alpha[i] != 0) { | |
114 if (!color_used[palette[i]]) { | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
115 level = (0xff * j) / nb_opaque_colors; |
2756 | 116 r = (((subtitle_color >> 16) & 0xff) * level) >> 8; |
117 g = (((subtitle_color >> 8) & 0xff) * level) >> 8; | |
118 b = (((subtitle_color >> 0) & 0xff) * level) >> 8; | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
119 rgba_palette[i] = b | (g << 8) | (r << 16) | ((alpha[i] * 17) << 24); |
2756 | 120 color_used[palette[i]] = (i + 1); |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
121 j--; |
2756 | 122 } else { |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
123 rgba_palette[i] = (rgba_palette[color_used[palette[i]] - 1] & 0x00ffffff) | |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
124 ((alpha[i] * 17) << 24); |
2756 | 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; | |
2797 | 136 int i; |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
137 int is_menu = 0; |
2756 | 138 |
139 if (buf_size < 4) | |
140 return -1; | |
2797 | 141 sub_header->rects = NULL; |
142 sub_header->num_rects = 0; | |
2756 | 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: | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
165 /* menu subpicture */ |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
166 is_menu = 1; |
2756 | 167 break; |
168 case 0x01: | |
169 /* set start date */ | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
170 sub_header->start_display_time = (date << 10) / 90; |
2756 | 171 break; |
172 case 0x02: | |
173 /* set end date */ | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
174 sub_header->end_display_time = (date << 10) / 90; |
2756 | 175 break; |
176 case 0x03: | |
177 /* set palette */ | |
178 if ((buf_size - pos) < 2) | |
179 goto fail; | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
180 palette[3] = buf[pos] >> 4; |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
181 palette[2] = buf[pos] & 0x0f; |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
182 palette[1] = buf[pos + 1] >> 4; |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
183 palette[0] = buf[pos + 1] & 0x0f; |
2756 | 184 pos += 2; |
185 break; | |
186 case 0x04: | |
187 /* set alpha */ | |
188 if ((buf_size - pos) < 2) | |
189 goto fail; | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
190 alpha[3] = buf[pos] >> 4; |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
191 alpha[2] = buf[pos] & 0x0f; |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
192 alpha[1] = buf[pos + 1] >> 4; |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
193 alpha[0] = buf[pos + 1] & 0x0f; |
2756 | 194 pos += 2; |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
195 #ifdef DEBUG |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
196 av_log(NULL, AV_LOG_INFO, "alpha=%x%x%x%x\n", alpha[0],alpha[1],alpha[2],alpha[3]); |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
197 #endif |
2756 | 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) { | |
2797 | 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 | |
2756 | 249 bitmap = av_malloc(w * h); |
2797 | 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); | |
2756 | 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); | |
2797 | 257 guess_palette(sub_header->rects[0].rgba_palette, |
2756 | 258 palette, alpha, 0xffff00); |
2797 | 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; | |
2756 | 266 } |
267 } | |
268 if (next_cmd_pos == cmd_pos) | |
269 break; | |
270 cmd_pos = next_cmd_pos; | |
271 } | |
2797 | 272 if (sub_header->num_rects > 0) |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
273 return is_menu; |
2756 | 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 */ | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
291 static int find_smallest_bounding_rectangle(AVSubtitle *s) |
2756 | 292 { |
293 uint8_t transp_color[256]; | |
294 int y1, y2, x1, x2, y, w, h, i; | |
295 uint8_t *bitmap; | |
296 | |
2797 | 297 if (s->num_rects == 0 || s->rects == NULL || s->rects[0].w <= 0 || s->rects[0].h <= 0) |
2756 | 298 return 0; |
299 | |
300 memset(transp_color, 0, 256); | |
2797 | 301 for(i = 0; i < s->rects[0].nb_colors; i++) { |
302 if ((s->rects[0].rgba_palette[i] >> 24) == 0) | |
2756 | 303 transp_color[i] = 1; |
304 } | |
305 y1 = 0; | |
2797 | 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)) | |
2756 | 308 y1++; |
2797 | 309 if (y1 == s->rects[0].h) { |
310 av_freep(&s->rects[0].bitmap); | |
311 s->rects[0].w = s->rects[0].h = 0; | |
2756 | 312 return 0; |
313 } | |
314 | |
2797 | 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)) | |
2756 | 318 y2--; |
319 x1 = 0; | |
2797 | 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)) | |
2756 | 322 x1++; |
2797 | 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, | |
2756 | 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++) { | |
2797 | 333 memcpy(bitmap + w * y, s->rects[0].bitmap + x1 + (y1 + y) * s->rects[0].linesize, w); |
2756 | 334 } |
2797 | 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; | |
2756 | 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; | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
384 int is_menu; |
2756 | 385 |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
386 is_menu = decode_dvd_subtitles(sub, buf, buf_size); |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
387 |
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
388 if (is_menu < 0) { |
2756 | 389 no_subtitle: |
390 *data_size = 0; | |
391 | |
392 return buf_size; | |
393 } | |
2833
1f117208d20f
subs.diff fixes a couple of minor bugs in my DVB subtitle decoder, and also fixes a few
michael
parents:
2797
diff
changeset
|
394 if (!is_menu && find_smallest_bounding_rectangle(sub) == 0) |
2756 | 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); | |
2797 | 401 ppm_save("/tmp/a.ppm", sub->rects[0].bitmap, |
402 sub->rects[0].w, sub->rects[0].h, sub->rects[0].rgba_palette); | |
2756 | 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 }; |