2 * DVD subtitle decoding
3 * Copyright (c) 2005 Fabrice Bellard
5 * This file is part of FFmpeg.
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.
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.
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
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"
33 typedef struct DVDSubContext
50 static void yuv_a_to_rgba(const uint8_t *ycbcr
, const uint8_t *alpha
, uint32_t *rgba
, int num_values
)
52 const uint8_t *cm
= ff_crop_tab
+ MAX_NEG_CROP
;
55 int r_add
, g_add
, b_add
;
57 for (i
= num_values
; i
> 0; i
--) {
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
;
67 static int decode_run_2bit(GetBitContext
*gb
, int *color
)
72 for (t
= 1; v
< t
&& t
<= 0x40; t
<<= 2)
73 v
= (v
<< 4) | get_bits(gb
, 4);
75 if (v
< 4) { /* Code for fill rest of line */
81 static int decode_run_8bit(GetBitContext
*gb
, int *color
)
84 int has_run
= get_bits1(gb
);
86 *color
= get_bits(gb
, 8);
88 *color
= get_bits(gb
, 2);
91 len
= get_bits(gb
, 7);
97 len
= get_bits(gb
, 3) + 2;
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
)
108 int x
, y
, len
, color
;
111 if (start
>= buf_size
)
114 if (w
<= 0 || h
<= 0)
117 bit_len
= (buf_size
- start
) * 8;
118 init_get_bits(&gb
, buf
+ start
, bit_len
);
124 if (get_bits_count(&gb
) > bit_len
)
127 len
= decode_run_8bit(&gb
, &color
);
129 len
= decode_run_2bit(&gb
, &color
);
130 len
= FFMIN(len
, w
- x
);
131 memset(d
+ x
, color
, len
);
146 static void guess_palette(DVDSubContext
* ctx
,
147 uint32_t *rgba_palette
,
148 uint32_t subtitle_color
)
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
156 {0x00, 0x55, 0xaa, 0xff},
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
;
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);
169 for(i
= 0; i
< 4; i
++)
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;
180 if (nb_opaque_colors
== 0)
184 memset(color_used
, 0, 16);
185 for(i
= 0; i
< 4; i
++) {
187 if (!color_used
[colormap
[i
]]) {
188 level
= level_map
[nb_opaque_colors
][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
] * 17) << 24);
193 color_used
[colormap
[i
]] = (i
+ 1);
196 rgba_palette
[i
] = (rgba_palette
[color_used
[colormap
[i
]] - 1] & 0x00ffffff) |
197 ((alpha
[i
] * 17) << 24);
203 static void reset_rects(AVSubtitle
*sub_header
)
207 if (sub_header
->rects
) {
208 for (i
= 0; i
< sub_header
->num_rects
; i
++) {
209 av_freep(&sub_header
->rects
[i
]->pict
.data
[0]);
210 av_freep(&sub_header
->rects
[i
]->pict
.data
[1]);
211 av_freep(&sub_header
->rects
[i
]);
213 av_freep(&sub_header
->rects
);
214 sub_header
->num_rects
= 0;
218 #define READ_OFFSET(a) (big_offsets ? AV_RB32(a) : AV_RB16(a))
220 static int decode_dvd_subtitles(DVDSubContext
*ctx
, AVSubtitle
*sub_header
,
221 const uint8_t *buf
, int buf_size
)
223 int cmd_pos
, pos
, cmd
, x1
, y1
, x2
, y2
, offset1
, offset2
, 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
;
234 if (AV_RB16(buf
) == 0) { /* HD subpicture with 4-byte offsets */
244 cmd_pos
= READ_OFFSET(buf
+ cmd_pos
);
246 if (cmd_pos
< 0 || cmd_pos
> buf_size
- 2 - offset_size
)
247 return AVERROR(EAGAIN
);
249 while (cmd_pos
> 0 && cmd_pos
< buf_size
- 2 - offset_size
) {
250 date
= AV_RB16(buf
+ cmd_pos
);
251 next_cmd_pos
= READ_OFFSET(buf
+ cmd_pos
+ 2);
252 av_dlog(NULL
, "cmd_pos=0x%04x next=0x%04x date=%d\n",
253 cmd_pos
, next_cmd_pos
, date
);
254 pos
= cmd_pos
+ 2 + offset_size
;
257 x1
= y1
= x2
= y2
= 0;
258 while (pos
< buf_size
) {
260 av_dlog(NULL
, "cmd=%02x\n", cmd
);
263 /* menu subpicture */
268 sub_header
->start_display_time
= (date
<< 10) / 90;
272 sub_header
->end_display_time
= (date
<< 10) / 90;
276 if ((buf_size
- pos
) < 2)
278 colormap
[3] = buf
[pos
] >> 4;
279 colormap
[2] = buf
[pos
] & 0x0f;
280 colormap
[1] = buf
[pos
+ 1] >> 4;
281 colormap
[0] = buf
[pos
+ 1] & 0x0f;
286 if ((buf_size
- pos
) < 2)
288 alpha
[3] = buf
[pos
] >> 4;
289 alpha
[2] = buf
[pos
] & 0x0f;
290 alpha
[1] = buf
[pos
+ 1] >> 4;
291 alpha
[0] = buf
[pos
+ 1] & 0x0f;
293 av_dlog(NULL
, "alpha=%x%x%x%x\n", alpha
[0],alpha
[1],alpha
[2],alpha
[3]);
297 if ((buf_size
- pos
) < 6)
299 x1
= (buf
[pos
] << 4) | (buf
[pos
+ 1] >> 4);
300 x2
= ((buf
[pos
+ 1] & 0x0f) << 8) | buf
[pos
+ 2];
301 y1
= (buf
[pos
+ 3] << 4) | (buf
[pos
+ 4] >> 4);
302 y2
= ((buf
[pos
+ 4] & 0x0f) << 8) | buf
[pos
+ 5];
305 av_dlog(NULL
, "x1=%d x2=%d y1=%d y2=%d\n", x1
, x2
, y1
, y2
);
309 if ((buf_size
- pos
) < 4)
311 offset1
= AV_RB16(buf
+ pos
);
312 offset2
= AV_RB16(buf
+ pos
+ 2);
313 av_dlog(NULL
, "offset1=0x%04x offset2=0x%04x\n", offset1
, offset2
);
317 if ((buf_size
- pos
) < 8)
319 offset1
= AV_RB32(buf
+ pos
);
320 offset2
= AV_RB32(buf
+ pos
+ 4);
321 av_dlog(NULL
, "offset1=0x%04x offset2=0x%04x\n", offset1
, offset2
);
327 if ((buf_size
- pos
) < 768)
329 yuv_palette
= buf
+ pos
;
333 /* HD set contrast (alpha) */
334 if ((buf_size
- pos
) < 256)
336 for (i
= 0; i
< 256; i
++)
337 alpha
[i
] = 0xFF - buf
[pos
+i
];
344 av_dlog(NULL
, "unrecognised subpicture command 0x%x\n", cmd
);
353 /* decode the bitmap */
360 if (w
> 0 && h
> 0) {
361 reset_rects(sub_header
);
363 bitmap
= av_malloc(w
* h
);
364 sub_header
->rects
= av_mallocz(sizeof(*sub_header
->rects
));
365 sub_header
->rects
[0] = av_mallocz(sizeof(AVSubtitleRect
));
366 sub_header
->num_rects
= 1;
367 sub_header
->rects
[0]->pict
.data
[0] = bitmap
;
368 if (decode_rle(bitmap
, w
* 2, w
, (h
+ 1) / 2,
369 buf
, offset1
, buf_size
, is_8bit
) < 0)
371 if (decode_rle(bitmap
+ w
, w
* 2, w
, h
/ 2,
372 buf
, offset2
, buf_size
, is_8bit
) < 0)
374 sub_header
->rects
[0]->pict
.data
[1] = av_mallocz(AVPALETTE_SIZE
);
378 sub_header
->rects
[0]->nb_colors
= 256;
379 yuv_a_to_rgba(yuv_palette
, alpha
, (uint32_t*)sub_header
->rects
[0]->pict
.data
[1], 256);
381 sub_header
->rects
[0]->nb_colors
= 4;
382 guess_palette(ctx
, (uint32_t*)sub_header
->rects
[0]->pict
.data
[1],
385 sub_header
->rects
[0]->x
= x1
;
386 sub_header
->rects
[0]->y
= y1
;
387 sub_header
->rects
[0]->w
= w
;
388 sub_header
->rects
[0]->h
= h
;
389 sub_header
->rects
[0]->type
= SUBTITLE_BITMAP
;
390 sub_header
->rects
[0]->pict
.linesize
[0] = w
;
391 sub_header
->rects
[0]->flags
= is_menu
? AV_SUBTITLE_FLAG_FORCED
: 0;
394 if (next_cmd_pos
< cmd_pos
) {
395 av_log(NULL
, AV_LOG_ERROR
, "Invalid command offset\n");
398 if (next_cmd_pos
== cmd_pos
)
400 cmd_pos
= next_cmd_pos
;
402 if (sub_header
->num_rects
> 0)
405 reset_rects(sub_header
);
409 static int is_transp(const uint8_t *buf
, int pitch
, int n
,
410 const uint8_t *transp_color
)
413 for(i
= 0; i
< n
; i
++) {
414 if (!transp_color
[*buf
])
421 /* return 0 if empty rectangle, 1 if non empty */
422 static int find_smallest_bounding_rectangle(AVSubtitle
*s
)
424 uint8_t transp_color
[256] = { 0 };
425 int y1
, y2
, x1
, x2
, y
, w
, h
, i
;
428 if (s
->num_rects
== 0 || !s
->rects
|| s
->rects
[0]->w
<= 0 || s
->rects
[0]->h
<= 0)
431 for(i
= 0; i
< s
->rects
[0]->nb_colors
; i
++) {
432 if ((((uint32_t*)s
->rects
[0]->pict
.data
[1])[i
] >> 24) == 0)
436 while (y1
< s
->rects
[0]->h
&& is_transp(s
->rects
[0]->pict
.data
[0] + y1
* s
->rects
[0]->pict
.linesize
[0],
437 1, s
->rects
[0]->w
, transp_color
))
439 if (y1
== s
->rects
[0]->h
) {
440 av_freep(&s
->rects
[0]->pict
.data
[0]);
441 s
->rects
[0]->w
= s
->rects
[0]->h
= 0;
445 y2
= s
->rects
[0]->h
- 1;
446 while (y2
> 0 && is_transp(s
->rects
[0]->pict
.data
[0] + y2
* s
->rects
[0]->pict
.linesize
[0], 1,
447 s
->rects
[0]->w
, transp_color
))
450 while (x1
< (s
->rects
[0]->w
- 1) && is_transp(s
->rects
[0]->pict
.data
[0] + x1
, s
->rects
[0]->pict
.linesize
[0],
451 s
->rects
[0]->h
, transp_color
))
453 x2
= s
->rects
[0]->w
- 1;
454 while (x2
> 0 && is_transp(s
->rects
[0]->pict
.data
[0] + x2
, s
->rects
[0]->pict
.linesize
[0], s
->rects
[0]->h
,
459 bitmap
= av_malloc(w
* h
);
462 for(y
= 0; y
< h
; y
++) {
463 memcpy(bitmap
+ w
* y
, s
->rects
[0]->pict
.data
[0] + x1
+ (y1
+ y
) * s
->rects
[0]->pict
.linesize
[0], w
);
465 av_freep(&s
->rects
[0]->pict
.data
[0]);
466 s
->rects
[0]->pict
.data
[0] = bitmap
;
467 s
->rects
[0]->pict
.linesize
[0] = w
;
470 s
->rects
[0]->x
+= x1
;
471 s
->rects
[0]->y
+= y1
;
476 #define ALPHA_MIX(A,BACK,FORE) (((255-(A)) * (BACK) + (A) * (FORE)) / 255)
477 static void ppm_save(const char *filename
, uint8_t *bitmap
, int w
, int h
,
478 uint32_t *rgba_palette
)
482 int back
[3] = {0, 255, 0}; /* green background */
485 f
= fopen(filename
, "w");
494 for(y
= 0; y
< h
; y
++) {
495 for(x
= 0; x
< w
; x
++) {
496 v
= rgba_palette
[bitmap
[y
* w
+ x
]];
498 putc(ALPHA_MIX(alpha
, back
[0], (v
>> 16) & 0xff), f
);
499 putc(ALPHA_MIX(alpha
, back
[1], (v
>> 8) & 0xff), f
);
500 putc(ALPHA_MIX(alpha
, back
[2], (v
>> 0) & 0xff), f
);
507 static int append_to_cached_buf(AVCodecContext
*avctx
,
508 const uint8_t *buf
, int buf_size
)
510 DVDSubContext
*ctx
= avctx
->priv_data
;
512 if (ctx
->buf_size
>= sizeof(ctx
->buf
) - buf_size
) {
513 av_log(avctx
, AV_LOG_WARNING
, "Attempt to reconstruct "
514 "too large SPU packets aborted.\n");
515 return AVERROR_INVALIDDATA
;
517 memcpy(ctx
->buf
+ ctx
->buf_size
, buf
, buf_size
);
518 ctx
->buf_size
+= buf_size
;
522 static int dvdsub_decode(AVCodecContext
*avctx
,
523 void *data
, int *data_size
,
526 DVDSubContext
*ctx
= avctx
->priv_data
;
527 const uint8_t *buf
= avpkt
->data
;
528 int buf_size
= avpkt
->size
;
529 AVSubtitle
*sub
= data
;
533 int ret
= append_to_cached_buf(avctx
, buf
, buf_size
);
539 buf_size
= ctx
->buf_size
;
542 is_menu
= decode_dvd_subtitles(ctx
, sub
, buf
, buf_size
);
543 if (is_menu
== AVERROR(EAGAIN
)) {
545 return append_to_cached_buf(avctx
, buf
, buf_size
);
555 if (!is_menu
&& find_smallest_bounding_rectangle(sub
) == 0)
558 if (ctx
->forced_subs_only
&& !(sub
->rects
[0]->flags
& AV_SUBTITLE_FLAG_FORCED
))
565 snprintf(ppm_name
, sizeof(ppm_name
), "/tmp/%05d.ppm", ctx
->sub_id
++);
566 av_dlog(NULL
, "start=%d ms end =%d ms\n",
567 sub
->start_display_time
,
568 sub
->end_display_time
);
569 ppm_save(ppm_name
, sub
->rects
[0]->pict
.data
[0],
570 sub
->rects
[0]->w
, sub
->rects
[0]->h
, (uint32_t*) sub
->rects
[0]->pict
.data
[1]);
579 static void parse_palette(DVDSubContext
*ctx
, char *p
)
583 ctx
->has_palette
= 1;
585 ctx
->palette
[i
] = strtoul(p
, &p
, 16);
586 while(*p
== ',' || av_isspace(*p
))
591 static int parse_ifo_palette(DVDSubContext
*ctx
, char *p
)
595 uint32_t sp_pgci
, pgci
, off_pgc
, pgc
;
596 uint8_t r
, g
, b
, yuv
[65], *buf
;
597 int i
, y
, cb
, cr
, r_add
, g_add
, b_add
;
599 const uint8_t *cm
= ff_crop_tab
+ MAX_NEG_CROP
;
601 ctx
->has_palette
= 0;
602 if ((ifo
= fopen(p
, "r")) == NULL
) {
603 av_log(ctx
, AV_LOG_WARNING
, "Unable to open IFO file \"%s\": %s\n", p
, strerror(errno
));
606 if (fread(ifostr
, 12, 1, ifo
) != 1 || memcmp(ifostr
, "DVDVIDEO-VTS", 12)) {
607 av_log(ctx
, AV_LOG_WARNING
, "\"%s\" is not a proper IFO file\n", p
);
608 ret
= AVERROR_INVALIDDATA
;
611 if (fseek(ifo
, 0xCC, SEEK_SET
) == -1) {
612 ret
= AVERROR(errno
);
615 if (fread(&sp_pgci
, 4, 1, ifo
) == 1) {
616 pgci
= av_be2ne32(sp_pgci
) * 2048;
617 if (fseek(ifo
, pgci
+ 0x0C, SEEK_SET
) == -1) {
618 ret
= AVERROR(errno
);
621 if (fread(&off_pgc
, 4, 1, ifo
) == 1) {
622 pgc
= pgci
+ av_be2ne32(off_pgc
);
623 if (fseek(ifo
, pgc
+ 0xA4, SEEK_SET
) == -1) {
624 ret
= AVERROR(errno
);
627 if (fread(yuv
, 64, 1, ifo
) == 1) {
629 for(i
=0; i
<16; i
++) {
633 YUV_TO_RGB1_CCIR(cb
, cr
);
634 YUV_TO_RGB2_CCIR(r
, g
, b
, y
);
635 ctx
->palette
[i
] = (r
<< 16) + (g
<< 8) + b
;
638 ctx
->has_palette
= 1;
642 if (ctx
->has_palette
== 0) {
643 av_log(ctx
, AV_LOG_WARNING
, "Failed to read palette from IFO file \"%s\"\n", p
);
644 ret
= AVERROR_INVALIDDATA
;
651 static int dvdsub_parse_extradata(AVCodecContext
*avctx
)
653 DVDSubContext
*ctx
= (DVDSubContext
*) avctx
->priv_data
;
654 char *dataorig
, *data
;
657 if (!avctx
->extradata
|| !avctx
->extradata_size
)
660 dataorig
= data
= av_malloc(avctx
->extradata_size
+1);
662 return AVERROR(ENOMEM
);
663 memcpy(data
, avctx
->extradata
, avctx
->extradata_size
);
664 data
[avctx
->extradata_size
] = '\0';
667 int pos
= strcspn(data
, "\n\r");
668 if (pos
==0 && *data
==0)
671 if (strncmp("palette:", data
, 8) == 0) {
672 parse_palette(ctx
, data
+ 8);
673 } else if (strncmp("size:", data
, 5) == 0) {
675 if (sscanf(data
+ 5, "%dx%d", &w
, &h
) == 2) {
676 ret
= ff_set_dimensions(avctx
, w
, h
);
683 data
+= strspn(data
, "\n\r");
691 static av_cold
int dvdsub_init(AVCodecContext
*avctx
)
693 DVDSubContext
*ctx
= avctx
->priv_data
;
696 if ((ret
= dvdsub_parse_extradata(avctx
)) < 0)
700 parse_ifo_palette(ctx
, ctx
->ifo_str
);
701 if (ctx
->palette_str
)
702 parse_palette(ctx
, ctx
->palette_str
);
703 if (ctx
->has_palette
) {
705 av_log(avctx
, AV_LOG_DEBUG
, "palette:");
707 av_log(avctx
, AV_LOG_DEBUG
, " 0x%06x", ctx
->palette
[i
]);
708 av_log(avctx
, AV_LOG_DEBUG
, "\n");
714 static av_cold
int dvdsub_close(AVCodecContext
*avctx
)
716 DVDSubContext
*ctx
= avctx
->priv_data
;
721 #define OFFSET(field) offsetof(DVDSubContext, field)
722 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
723 static const AVOption options
[] = {
724 { "palette", "set the global palette", OFFSET(palette_str
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, 0, 0, SD
},
725 { "ifo_palette", "obtain the global palette from .IFO file", OFFSET(ifo_str
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, 0, 0, SD
},
726 { "forced_subs_only", "Only show forced subtitles", OFFSET(forced_subs_only
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, SD
},
729 static const AVClass dvdsub_class
= {
730 .class_name
= "dvdsubdec",
731 .item_name
= av_default_item_name
,
733 .version
= LIBAVUTIL_VERSION_INT
,
736 AVCodec ff_dvdsub_decoder
= {
738 .long_name
= NULL_IF_CONFIG_SMALL("DVD subtitles"),
739 .type
= AVMEDIA_TYPE_SUBTITLE
,
740 .id
= AV_CODEC_ID_DVD_SUBTITLE
,
741 .priv_data_size
= sizeof(DVDSubContext
),
743 .decode
= dvdsub_decode
,
744 .close
= dvdsub_close
,
745 .priv_class
= &dvdsub_class
,