blob: 4e9c0580f4cfd0e689001b38564cd403cd535c5b
1 | /* |
2 | * DVD subtitle decoding |
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 | |
22 | #include "avcodec.h" |
23 | #include "get_bits.h" |
24 | #include "internal.h" |
25 | |
26 | #include "libavutil/attributes.h" |
27 | #include "libavutil/colorspace.h" |
28 | #include "libavutil/opt.h" |
29 | #include "libavutil/imgutils.h" |
30 | #include "libavutil/avstring.h" |
31 | #include "libavutil/bswap.h" |
32 | |
33 | typedef struct DVDSubContext |
34 | { |
35 | AVClass *class; |
36 | uint32_t palette[16]; |
37 | char *palette_str; |
38 | char *ifo_str; |
39 | int has_palette; |
40 | uint8_t colormap[4]; |
41 | uint8_t alpha[256]; |
42 | uint8_t buf[0x10000]; |
43 | int buf_size; |
44 | int forced_subs_only; |
45 | #ifdef DEBUG |
46 | int sub_id; |
47 | #endif |
48 | } DVDSubContext; |
49 | |
50 | static void yuv_a_to_rgba(const uint8_t *ycbcr, const uint8_t *alpha, uint32_t *rgba, int num_values) |
51 | { |
52 | const uint8_t *cm = ff_crop_tab + MAX_NEG_CROP; |
53 | uint8_t r, g, b; |
54 | int i, y, cb, cr; |
55 | int r_add, g_add, b_add; |
56 | |
57 | for (i = num_values; i > 0; i--) { |
58 | y = *ycbcr++; |
59 | cr = *ycbcr++; |
60 | cb = *ycbcr++; |
61 | YUV_TO_RGB1_CCIR(cb, cr); |
62 | YUV_TO_RGB2_CCIR(r, g, b, y); |
63 | *rgba++ = (*alpha++ << 24) | (r << 16) | (g << 8) | b; |
64 | } |
65 | } |
66 | |
67 | static int decode_run_2bit(GetBitContext *gb, int *color) |
68 | { |
69 | unsigned int v, t; |
70 | |
71 | v = 0; |
72 | for (t = 1; v < t && t <= 0x40; t <<= 2) |
73 | v = (v << 4) | get_bits(gb, 4); |
74 | *color = v & 3; |
75 | if (v < 4) { /* Code for fill rest of line */ |
76 | return INT_MAX; |
77 | } |
78 | return v >> 2; |
79 | } |
80 | |
81 | static int decode_run_8bit(GetBitContext *gb, int *color) |
82 | { |
83 | int len; |
84 | int has_run = get_bits1(gb); |
85 | if (get_bits1(gb)) |
86 | *color = get_bits(gb, 8); |
87 | else |
88 | *color = get_bits(gb, 2); |
89 | if (has_run) { |
90 | if (get_bits1(gb)) { |
91 | len = get_bits(gb, 7); |
92 | if (len == 0) |
93 | len = INT_MAX; |
94 | else |
95 | len += 9; |
96 | } else |
97 | len = get_bits(gb, 3) + 2; |
98 | } else |
99 | len = 1; |
100 | return len; |
101 | } |
102 | |
103 | static int decode_rle(uint8_t *bitmap, int linesize, int w, int h, |
104 | const uint8_t *buf, int start, int buf_size, int is_8bit) |
105 | { |
106 | GetBitContext gb; |
107 | int bit_len; |
108 | int x, y, len, color; |
109 | uint8_t *d; |
110 | |
111 | if (start >= buf_size) |
112 | return -1; |
113 | |
114 | if (w <= 0 || h <= 0) |
115 | return -1; |
116 | |
117 | bit_len = (buf_size - start) * 8; |
118 | init_get_bits(&gb, buf + start, bit_len); |
119 | |
120 | x = 0; |
121 | y = 0; |
122 | d = bitmap; |
123 | for(;;) { |
124 | if (get_bits_count(&gb) > bit_len) |
125 | return -1; |
126 | if (is_8bit) |
127 | len = decode_run_8bit(&gb, &color); |
128 | else |
129 | len = decode_run_2bit(&gb, &color); |
130 | len = FFMIN(len, w - x); |
131 | memset(d + x, color, len); |
132 | x += len; |
133 | if (x >= w) { |
134 | y++; |
135 | if (y >= h) |
136 | break; |
137 | d += linesize; |
138 | x = 0; |
139 | /* byte align */ |
140 | align_get_bits(&gb); |
141 | } |
142 | } |
143 | return 0; |
144 | } |
145 | |
146 | static void guess_palette(DVDSubContext* ctx, |
147 | uint32_t *rgba_palette, |
148 | uint32_t subtitle_color) |
149 | { |
150 | static const uint8_t level_map[4][4] = { |
151 | // this configuration (full range, lowest to highest) in tests |
152 | // seemed most common, so assume this |
153 | {0xff}, |
154 | {0x00, 0xff}, |
155 | {0x00, 0x80, 0xff}, |
156 | {0x00, 0x55, 0xaa, 0xff}, |
157 | }; |
158 | uint8_t color_used[16] = { 0 }; |
159 | int nb_opaque_colors, i, level, j, r, g, b; |
160 | uint8_t *colormap = ctx->colormap, *alpha = ctx->alpha; |
161 | |
162 | if(ctx->has_palette) { |
163 | for(i = 0; i < 4; i++) |
164 | rgba_palette[i] = (ctx->palette[colormap[i]] & 0x00ffffff) |
165 | | ((alpha[i] * 17U) << 24); |
166 | return; |
167 | } |
168 | |
169 | for(i = 0; i < 4; i++) |
170 | rgba_palette[i] = 0; |
171 | |
172 | nb_opaque_colors = 0; |
173 | for(i = 0; i < 4; i++) { |
174 | if (alpha[i] != 0 && !color_used[colormap[i]]) { |
175 | color_used[colormap[i]] = 1; |
176 | nb_opaque_colors++; |
177 | } |
178 | } |
179 | |
180 | if (nb_opaque_colors == 0) |
181 | return; |
182 | |
183 | j = 0; |
184 | memset(color_used, 0, 16); |
185 | for(i = 0; i < 4; i++) { |
186 | if (alpha[i] != 0) { |
187 | if (!color_used[colormap[i]]) { |
188 | level = level_map[nb_opaque_colors - 1][j]; |
189 | r = (((subtitle_color >> 16) & 0xff) * level) >> 8; |
190 | g = (((subtitle_color >> 8) & 0xff) * level) >> 8; |
191 | b = (((subtitle_color >> 0) & 0xff) * level) >> 8; |
192 | rgba_palette[i] = b | (g << 8) | (r << 16) | ((alpha[i] * 17U) << 24); |
193 | color_used[colormap[i]] = (i + 1); |
194 | j++; |
195 | } else { |
196 | rgba_palette[i] = (rgba_palette[color_used[colormap[i]] - 1] & 0x00ffffff) | |
197 | ((alpha[i] * 17U) << 24); |
198 | } |
199 | } |
200 | } |
201 | } |
202 | |
203 | static void reset_rects(AVSubtitle *sub_header) |
204 | { |
205 | int i; |
206 | |
207 | if (sub_header->rects) { |
208 | for (i = 0; i < sub_header->num_rects; i++) { |
209 | av_freep(&sub_header->rects[i]->data[0]); |
210 | av_freep(&sub_header->rects[i]->data[1]); |
211 | av_freep(&sub_header->rects[i]); |
212 | } |
213 | av_freep(&sub_header->rects); |
214 | sub_header->num_rects = 0; |
215 | } |
216 | } |
217 | |
218 | #define READ_OFFSET(a) (big_offsets ? AV_RB32(a) : AV_RB16(a)) |
219 | |
220 | static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header, |
221 | const uint8_t *buf, int buf_size) |
222 | { |
223 | int cmd_pos, pos, cmd, x1, y1, x2, y2, next_cmd_pos; |
224 | int big_offsets, offset_size, is_8bit = 0; |
225 | const uint8_t *yuv_palette = NULL; |
226 | uint8_t *colormap = ctx->colormap, *alpha = ctx->alpha; |
227 | int date; |
228 | int i; |
229 | int is_menu = 0; |
230 | uint32_t size; |
231 | int64_t offset1, offset2; |
232 | |
233 | if (buf_size < 10) |
234 | return -1; |
235 | |
236 | if (AV_RB16(buf) == 0) { /* HD subpicture with 4-byte offsets */ |
237 | big_offsets = 1; |
238 | offset_size = 4; |
239 | cmd_pos = 6; |
240 | } else { |
241 | big_offsets = 0; |
242 | offset_size = 2; |
243 | cmd_pos = 2; |
244 | } |
245 | |
246 | size = READ_OFFSET(buf + (big_offsets ? 2 : 0)); |
247 | cmd_pos = READ_OFFSET(buf + cmd_pos); |
248 | |
249 | if (cmd_pos < 0 || cmd_pos > buf_size - 2 - offset_size) { |
250 | if (cmd_pos > size) { |
251 | av_log(ctx, AV_LOG_ERROR, "Discarding invalid packet\n"); |
252 | return 0; |
253 | } |
254 | return AVERROR(EAGAIN); |
255 | } |
256 | |
257 | while (cmd_pos > 0 && cmd_pos < buf_size - 2 - offset_size) { |
258 | date = AV_RB16(buf + cmd_pos); |
259 | next_cmd_pos = READ_OFFSET(buf + cmd_pos + 2); |
260 | ff_dlog(NULL, "cmd_pos=0x%04x next=0x%04x date=%d\n", |
261 | cmd_pos, next_cmd_pos, date); |
262 | pos = cmd_pos + 2 + offset_size; |
263 | offset1 = -1; |
264 | offset2 = -1; |
265 | x1 = y1 = x2 = y2 = 0; |
266 | while (pos < buf_size) { |
267 | cmd = buf[pos++]; |
268 | ff_dlog(NULL, "cmd=%02x\n", cmd); |
269 | switch(cmd) { |
270 | case 0x00: |
271 | /* menu subpicture */ |
272 | is_menu = 1; |
273 | break; |
274 | case 0x01: |
275 | /* set start date */ |
276 | sub_header->start_display_time = (date << 10) / 90; |
277 | break; |
278 | case 0x02: |
279 | /* set end date */ |
280 | sub_header->end_display_time = (date << 10) / 90; |
281 | break; |
282 | case 0x03: |
283 | /* set colormap */ |
284 | if ((buf_size - pos) < 2) |
285 | goto fail; |
286 | colormap[3] = buf[pos] >> 4; |
287 | colormap[2] = buf[pos] & 0x0f; |
288 | colormap[1] = buf[pos + 1] >> 4; |
289 | colormap[0] = buf[pos + 1] & 0x0f; |
290 | pos += 2; |
291 | break; |
292 | case 0x04: |
293 | /* set alpha */ |
294 | if ((buf_size - pos) < 2) |
295 | goto fail; |
296 | alpha[3] = buf[pos] >> 4; |
297 | alpha[2] = buf[pos] & 0x0f; |
298 | alpha[1] = buf[pos + 1] >> 4; |
299 | alpha[0] = buf[pos + 1] & 0x0f; |
300 | pos += 2; |
301 | ff_dlog(NULL, "alpha=%x%x%x%x\n", alpha[0],alpha[1],alpha[2],alpha[3]); |
302 | break; |
303 | case 0x05: |
304 | case 0x85: |
305 | if ((buf_size - pos) < 6) |
306 | goto fail; |
307 | x1 = (buf[pos] << 4) | (buf[pos + 1] >> 4); |
308 | x2 = ((buf[pos + 1] & 0x0f) << 8) | buf[pos + 2]; |
309 | y1 = (buf[pos + 3] << 4) | (buf[pos + 4] >> 4); |
310 | y2 = ((buf[pos + 4] & 0x0f) << 8) | buf[pos + 5]; |
311 | if (cmd & 0x80) |
312 | is_8bit = 1; |
313 | ff_dlog(NULL, "x1=%d x2=%d y1=%d y2=%d\n", x1, x2, y1, y2); |
314 | pos += 6; |
315 | break; |
316 | case 0x06: |
317 | if ((buf_size - pos) < 4) |
318 | goto fail; |
319 | offset1 = AV_RB16(buf + pos); |
320 | offset2 = AV_RB16(buf + pos + 2); |
321 | ff_dlog(NULL, "offset1=0x%04"PRIx64" offset2=0x%04"PRIx64"\n", offset1, offset2); |
322 | pos += 4; |
323 | break; |
324 | case 0x86: |
325 | if ((buf_size - pos) < 8) |
326 | goto fail; |
327 | offset1 = AV_RB32(buf + pos); |
328 | offset2 = AV_RB32(buf + pos + 4); |
329 | ff_dlog(NULL, "offset1=0x%04"PRIx64" offset2=0x%04"PRIx64"\n", offset1, offset2); |
330 | pos += 8; |
331 | break; |
332 | |
333 | case 0x83: |
334 | /* HD set palette */ |
335 | if ((buf_size - pos) < 768) |
336 | goto fail; |
337 | yuv_palette = buf + pos; |
338 | pos += 768; |
339 | break; |
340 | case 0x84: |
341 | /* HD set contrast (alpha) */ |
342 | if ((buf_size - pos) < 256) |
343 | goto fail; |
344 | for (i = 0; i < 256; i++) |
345 | alpha[i] = 0xFF - buf[pos+i]; |
346 | pos += 256; |
347 | break; |
348 | |
349 | case 0xff: |
350 | goto the_end; |
351 | default: |
352 | ff_dlog(NULL, "unrecognised subpicture command 0x%x\n", cmd); |
353 | goto the_end; |
354 | } |
355 | } |
356 | the_end: |
357 | if (offset1 >= buf_size || offset2 >= buf_size) |
358 | goto fail; |
359 | |
360 | if (offset1 >= 0 && offset2 >= 0) { |
361 | int w, h; |
362 | uint8_t *bitmap; |
363 | |
364 | /* decode the bitmap */ |
365 | w = x2 - x1 + 1; |
366 | if (w < 0) |
367 | w = 0; |
368 | h = y2 - y1 + 1; |
369 | if (h < 0) |
370 | h = 0; |
371 | if (w > 0 && h > 1) { |
372 | reset_rects(sub_header); |
373 | |
374 | sub_header->rects = av_mallocz(sizeof(*sub_header->rects)); |
375 | if (!sub_header->rects) |
376 | goto fail; |
377 | sub_header->rects[0] = av_mallocz(sizeof(AVSubtitleRect)); |
378 | if (!sub_header->rects[0]) |
379 | goto fail; |
380 | sub_header->num_rects = 1; |
381 | bitmap = sub_header->rects[0]->data[0] = av_malloc(w * h); |
382 | if (!bitmap) |
383 | goto fail; |
384 | if (decode_rle(bitmap, w * 2, w, (h + 1) / 2, |
385 | buf, offset1, buf_size, is_8bit) < 0) |
386 | goto fail; |
387 | if (decode_rle(bitmap + w, w * 2, w, h / 2, |
388 | buf, offset2, buf_size, is_8bit) < 0) |
389 | goto fail; |
390 | sub_header->rects[0]->data[1] = av_mallocz(AVPALETTE_SIZE); |
391 | if (!sub_header->rects[0]->data[1]) |
392 | goto fail; |
393 | if (is_8bit) { |
394 | if (!yuv_palette) |
395 | goto fail; |
396 | sub_header->rects[0]->nb_colors = 256; |
397 | yuv_a_to_rgba(yuv_palette, alpha, |
398 | (uint32_t *)sub_header->rects[0]->data[1], |
399 | 256); |
400 | } else { |
401 | sub_header->rects[0]->nb_colors = 4; |
402 | guess_palette(ctx, (uint32_t*)sub_header->rects[0]->data[1], |
403 | 0xffff00); |
404 | } |
405 | sub_header->rects[0]->x = x1; |
406 | sub_header->rects[0]->y = y1; |
407 | sub_header->rects[0]->w = w; |
408 | sub_header->rects[0]->h = h; |
409 | sub_header->rects[0]->type = SUBTITLE_BITMAP; |
410 | sub_header->rects[0]->linesize[0] = w; |
411 | sub_header->rects[0]->flags = is_menu ? AV_SUBTITLE_FLAG_FORCED : 0; |
412 | |
413 | #if FF_API_AVPICTURE |
414 | FF_DISABLE_DEPRECATION_WARNINGS |
415 | for (i = 0; i < 4; i++) { |
416 | sub_header->rects[0]->pict.data[i] = sub_header->rects[0]->data[i]; |
417 | sub_header->rects[0]->pict.linesize[i] = sub_header->rects[0]->linesize[i]; |
418 | } |
419 | FF_ENABLE_DEPRECATION_WARNINGS |
420 | #endif |
421 | } |
422 | } |
423 | if (next_cmd_pos < cmd_pos) { |
424 | av_log(ctx, AV_LOG_ERROR, "Invalid command offset\n"); |
425 | break; |
426 | } |
427 | if (next_cmd_pos == cmd_pos) |
428 | break; |
429 | cmd_pos = next_cmd_pos; |
430 | } |
431 | if (sub_header->num_rects > 0) |
432 | return is_menu; |
433 | fail: |
434 | reset_rects(sub_header); |
435 | return -1; |
436 | } |
437 | |
438 | static int is_transp(const uint8_t *buf, int pitch, int n, |
439 | const uint8_t *transp_color) |
440 | { |
441 | int i; |
442 | for(i = 0; i < n; i++) { |
443 | if (!transp_color[*buf]) |
444 | return 0; |
445 | buf += pitch; |
446 | } |
447 | return 1; |
448 | } |
449 | |
450 | /* return 0 if empty rectangle, 1 if non empty */ |
451 | static int find_smallest_bounding_rectangle(AVSubtitle *s) |
452 | { |
453 | uint8_t transp_color[256] = { 0 }; |
454 | int y1, y2, x1, x2, y, w, h, i; |
455 | uint8_t *bitmap; |
456 | |
457 | if (s->num_rects == 0 || !s->rects || s->rects[0]->w <= 0 || s->rects[0]->h <= 0) |
458 | return 0; |
459 | |
460 | for(i = 0; i < s->rects[0]->nb_colors; i++) { |
461 | if ((((uint32_t *)s->rects[0]->data[1])[i] >> 24) == 0) |
462 | transp_color[i] = 1; |
463 | } |
464 | y1 = 0; |
465 | while (y1 < s->rects[0]->h && is_transp(s->rects[0]->data[0] + y1 * s->rects[0]->linesize[0], |
466 | 1, s->rects[0]->w, transp_color)) |
467 | y1++; |
468 | if (y1 == s->rects[0]->h) { |
469 | av_freep(&s->rects[0]->data[0]); |
470 | s->rects[0]->w = s->rects[0]->h = 0; |
471 | return 0; |
472 | } |
473 | |
474 | y2 = s->rects[0]->h - 1; |
475 | while (y2 > 0 && is_transp(s->rects[0]->data[0] + y2 * s->rects[0]->linesize[0], 1, |
476 | s->rects[0]->w, transp_color)) |
477 | y2--; |
478 | x1 = 0; |
479 | while (x1 < (s->rects[0]->w - 1) && is_transp(s->rects[0]->data[0] + x1, s->rects[0]->linesize[0], |
480 | s->rects[0]->h, transp_color)) |
481 | x1++; |
482 | x2 = s->rects[0]->w - 1; |
483 | while (x2 > 0 && is_transp(s->rects[0]->data[0] + x2, s->rects[0]->linesize[0], s->rects[0]->h, |
484 | transp_color)) |
485 | x2--; |
486 | w = x2 - x1 + 1; |
487 | h = y2 - y1 + 1; |
488 | bitmap = av_malloc(w * h); |
489 | if (!bitmap) |
490 | return 1; |
491 | for(y = 0; y < h; y++) { |
492 | memcpy(bitmap + w * y, s->rects[0]->data[0] + x1 + (y1 + y) * s->rects[0]->linesize[0], w); |
493 | } |
494 | av_freep(&s->rects[0]->data[0]); |
495 | s->rects[0]->data[0] = bitmap; |
496 | s->rects[0]->linesize[0] = w; |
497 | s->rects[0]->w = w; |
498 | s->rects[0]->h = h; |
499 | s->rects[0]->x += x1; |
500 | s->rects[0]->y += y1; |
501 | |
502 | #if FF_API_AVPICTURE |
503 | FF_DISABLE_DEPRECATION_WARNINGS |
504 | for (i = 0; i < 4; i++) { |
505 | s->rects[0]->pict.data[i] = s->rects[0]->data[i]; |
506 | s->rects[0]->pict.linesize[i] = s->rects[0]->linesize[i]; |
507 | } |
508 | FF_ENABLE_DEPRECATION_WARNINGS |
509 | #endif |
510 | |
511 | return 1; |
512 | } |
513 | |
514 | #ifdef DEBUG |
515 | #define ALPHA_MIX(A,BACK,FORE) (((255-(A)) * (BACK) + (A) * (FORE)) / 255) |
516 | static void ppm_save(const char *filename, uint8_t *bitmap, int w, int h, |
517 | uint32_t *rgba_palette) |
518 | { |
519 | int x, y, alpha; |
520 | uint32_t v; |
521 | int back[3] = {0, 255, 0}; /* green background */ |
522 | FILE *f; |
523 | |
524 | f = fopen(filename, "w"); |
525 | if (!f) { |
526 | perror(filename); |
527 | return; |
528 | } |
529 | fprintf(f, "P6\n" |
530 | "%d %d\n" |
531 | "%d\n", |
532 | w, h, 255); |
533 | for(y = 0; y < h; y++) { |
534 | for(x = 0; x < w; x++) { |
535 | v = rgba_palette[bitmap[y * w + x]]; |
536 | alpha = v >> 24; |
537 | putc(ALPHA_MIX(alpha, back[0], (v >> 16) & 0xff), f); |
538 | putc(ALPHA_MIX(alpha, back[1], (v >> 8) & 0xff), f); |
539 | putc(ALPHA_MIX(alpha, back[2], (v >> 0) & 0xff), f); |
540 | } |
541 | } |
542 | fclose(f); |
543 | } |
544 | #endif |
545 | |
546 | static int append_to_cached_buf(AVCodecContext *avctx, |
547 | const uint8_t *buf, int buf_size) |
548 | { |
549 | DVDSubContext *ctx = avctx->priv_data; |
550 | |
551 | av_assert0(buf_size >= 0 && ctx->buf_size <= sizeof(ctx->buf)); |
552 | if (buf_size >= sizeof(ctx->buf) - ctx->buf_size) { |
553 | av_log(avctx, AV_LOG_WARNING, "Attempt to reconstruct " |
554 | "too large SPU packets aborted.\n"); |
555 | ctx->buf_size = 0; |
556 | return AVERROR_INVALIDDATA; |
557 | } |
558 | memcpy(ctx->buf + ctx->buf_size, buf, buf_size); |
559 | ctx->buf_size += buf_size; |
560 | return 0; |
561 | } |
562 | |
563 | static int dvdsub_decode(AVCodecContext *avctx, |
564 | void *data, int *data_size, |
565 | AVPacket *avpkt) |
566 | { |
567 | DVDSubContext *ctx = avctx->priv_data; |
568 | const uint8_t *buf = avpkt->data; |
569 | int buf_size = avpkt->size; |
570 | AVSubtitle *sub = data; |
571 | int appended = 0; |
572 | int is_menu; |
573 | |
574 | if (ctx->buf_size) { |
575 | int ret = append_to_cached_buf(avctx, buf, buf_size); |
576 | if (ret < 0) { |
577 | *data_size = 0; |
578 | return ret; |
579 | } |
580 | buf = ctx->buf; |
581 | buf_size = ctx->buf_size; |
582 | appended = 1; |
583 | } |
584 | |
585 | is_menu = decode_dvd_subtitles(ctx, sub, buf, buf_size); |
586 | if (is_menu == AVERROR(EAGAIN)) { |
587 | *data_size = 0; |
588 | return appended ? 0 : append_to_cached_buf(avctx, buf, buf_size); |
589 | } |
590 | |
591 | if (is_menu < 0) { |
592 | no_subtitle: |
593 | reset_rects(sub); |
594 | *data_size = 0; |
595 | |
596 | return buf_size; |
597 | } |
598 | if (!is_menu && find_smallest_bounding_rectangle(sub) == 0) |
599 | goto no_subtitle; |
600 | |
601 | if (ctx->forced_subs_only && !(sub->rects[0]->flags & AV_SUBTITLE_FLAG_FORCED)) |
602 | goto no_subtitle; |
603 | |
604 | #if defined(DEBUG) |
605 | { |
606 | char ppm_name[32]; |
607 | |
608 | snprintf(ppm_name, sizeof(ppm_name), "/tmp/%05d.ppm", ctx->sub_id++); |
609 | ff_dlog(NULL, "start=%d ms end =%d ms\n", |
610 | sub->start_display_time, |
611 | sub->end_display_time); |
612 | ppm_save(ppm_name, sub->rects[0]->data[0], |
613 | sub->rects[0]->w, sub->rects[0]->h, (uint32_t*) sub->rects[0]->data[1]); |
614 | } |
615 | #endif |
616 | |
617 | ctx->buf_size = 0; |
618 | *data_size = 1; |
619 | return buf_size; |
620 | } |
621 | |
622 | static void parse_palette(DVDSubContext *ctx, char *p) |
623 | { |
624 | int i; |
625 | |
626 | ctx->has_palette = 1; |
627 | for(i=0;i<16;i++) { |
628 | ctx->palette[i] = strtoul(p, &p, 16); |
629 | while(*p == ',' || av_isspace(*p)) |
630 | p++; |
631 | } |
632 | } |
633 | |
634 | static int parse_ifo_palette(DVDSubContext *ctx, char *p) |
635 | { |
636 | FILE *ifo; |
637 | char ifostr[12]; |
638 | uint32_t sp_pgci, pgci, off_pgc, pgc; |
639 | uint8_t r, g, b, yuv[65], *buf; |
640 | int i, y, cb, cr, r_add, g_add, b_add; |
641 | int ret = 0; |
642 | const uint8_t *cm = ff_crop_tab + MAX_NEG_CROP; |
643 | |
644 | ctx->has_palette = 0; |
645 | if ((ifo = fopen(p, "r")) == NULL) { |
646 | av_log(ctx, AV_LOG_WARNING, "Unable to open IFO file \"%s\": %s\n", p, av_err2str(AVERROR(errno))); |
647 | return AVERROR_EOF; |
648 | } |
649 | if (fread(ifostr, 12, 1, ifo) != 1 || memcmp(ifostr, "DVDVIDEO-VTS", 12)) { |
650 | av_log(ctx, AV_LOG_WARNING, "\"%s\" is not a proper IFO file\n", p); |
651 | ret = AVERROR_INVALIDDATA; |
652 | goto end; |
653 | } |
654 | if (fseek(ifo, 0xCC, SEEK_SET) == -1) { |
655 | ret = AVERROR(errno); |
656 | goto end; |
657 | } |
658 | if (fread(&sp_pgci, 4, 1, ifo) == 1) { |
659 | pgci = av_be2ne32(sp_pgci) * 2048; |
660 | if (fseek(ifo, pgci + 0x0C, SEEK_SET) == -1) { |
661 | ret = AVERROR(errno); |
662 | goto end; |
663 | } |
664 | if (fread(&off_pgc, 4, 1, ifo) == 1) { |
665 | pgc = pgci + av_be2ne32(off_pgc); |
666 | if (fseek(ifo, pgc + 0xA4, SEEK_SET) == -1) { |
667 | ret = AVERROR(errno); |
668 | goto end; |
669 | } |
670 | if (fread(yuv, 64, 1, ifo) == 1) { |
671 | buf = yuv; |
672 | for(i=0; i<16; i++) { |
673 | y = *++buf; |
674 | cr = *++buf; |
675 | cb = *++buf; |
676 | YUV_TO_RGB1_CCIR(cb, cr); |
677 | YUV_TO_RGB2_CCIR(r, g, b, y); |
678 | ctx->palette[i] = (r << 16) + (g << 8) + b; |
679 | buf++; |
680 | } |
681 | ctx->has_palette = 1; |
682 | } |
683 | } |
684 | } |
685 | if (ctx->has_palette == 0) { |
686 | av_log(ctx, AV_LOG_WARNING, "Failed to read palette from IFO file \"%s\"\n", p); |
687 | ret = AVERROR_INVALIDDATA; |
688 | } |
689 | end: |
690 | fclose(ifo); |
691 | return ret; |
692 | } |
693 | |
694 | static int dvdsub_parse_extradata(AVCodecContext *avctx) |
695 | { |
696 | DVDSubContext *ctx = (DVDSubContext*) avctx->priv_data; |
697 | char *dataorig, *data; |
698 | int ret = 1; |
699 | |
700 | if (!avctx->extradata || !avctx->extradata_size) |
701 | return 1; |
702 | |
703 | dataorig = data = av_malloc(avctx->extradata_size+1); |
704 | if (!data) |
705 | return AVERROR(ENOMEM); |
706 | memcpy(data, avctx->extradata, avctx->extradata_size); |
707 | data[avctx->extradata_size] = '\0'; |
708 | |
709 | for(;;) { |
710 | int pos = strcspn(data, "\n\r"); |
711 | if (pos==0 && *data==0) |
712 | break; |
713 | |
714 | if (strncmp("palette:", data, 8) == 0) { |
715 | parse_palette(ctx, data + 8); |
716 | } else if (strncmp("size:", data, 5) == 0) { |
717 | int w, h; |
718 | if (sscanf(data + 5, "%dx%d", &w, &h) == 2) { |
719 | ret = ff_set_dimensions(avctx, w, h); |
720 | if (ret < 0) |
721 | goto fail; |
722 | } |
723 | } |
724 | |
725 | data += pos; |
726 | data += strspn(data, "\n\r"); |
727 | } |
728 | |
729 | fail: |
730 | av_free(dataorig); |
731 | return ret; |
732 | } |
733 | |
734 | static av_cold int dvdsub_init(AVCodecContext *avctx) |
735 | { |
736 | DVDSubContext *ctx = avctx->priv_data; |
737 | int ret; |
738 | |
739 | if ((ret = dvdsub_parse_extradata(avctx)) < 0) |
740 | return ret; |
741 | |
742 | if (ctx->ifo_str) |
743 | parse_ifo_palette(ctx, ctx->ifo_str); |
744 | if (ctx->palette_str) |
745 | parse_palette(ctx, ctx->palette_str); |
746 | if (ctx->has_palette) { |
747 | int i; |
748 | av_log(avctx, AV_LOG_DEBUG, "palette:"); |
749 | for(i=0;i<16;i++) |
750 | av_log(avctx, AV_LOG_DEBUG, " 0x%06"PRIx32, ctx->palette[i]); |
751 | av_log(avctx, AV_LOG_DEBUG, "\n"); |
752 | } |
753 | |
754 | return 1; |
755 | } |
756 | |
757 | static void dvdsub_flush(AVCodecContext *avctx) |
758 | { |
759 | DVDSubContext *ctx = avctx->priv_data; |
760 | ctx->buf_size = 0; |
761 | } |
762 | |
763 | static av_cold int dvdsub_close(AVCodecContext *avctx) |
764 | { |
765 | dvdsub_flush(avctx); |
766 | return 0; |
767 | } |
768 | |
769 | #define OFFSET(field) offsetof(DVDSubContext, field) |
770 | #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM |
771 | static const AVOption options[] = { |
772 | { "palette", "set the global palette", OFFSET(palette_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD }, |
773 | { "ifo_palette", "obtain the global palette from .IFO file", OFFSET(ifo_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD }, |
774 | { "forced_subs_only", "Only show forced subtitles", OFFSET(forced_subs_only), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, SD}, |
775 | { NULL } |
776 | }; |
777 | static const AVClass dvdsub_class = { |
778 | .class_name = "dvdsubdec", |
779 | .item_name = av_default_item_name, |
780 | .option = options, |
781 | .version = LIBAVUTIL_VERSION_INT, |
782 | }; |
783 | |
784 | AVCodec ff_dvdsub_decoder = { |
785 | .name = "dvdsub", |
786 | .long_name = NULL_IF_CONFIG_SMALL("DVD subtitles"), |
787 | .type = AVMEDIA_TYPE_SUBTITLE, |
788 | .id = AV_CODEC_ID_DVD_SUBTITLE, |
789 | .priv_data_size = sizeof(DVDSubContext), |
790 | .init = dvdsub_init, |
791 | .decode = dvdsub_decode, |
792 | .flush = dvdsub_flush, |
793 | .close = dvdsub_close, |
794 | .priv_class = &dvdsub_class, |
795 | }; |
796 |