2 * DVD subtitle decoding for ffmpeg
3 * Copyright (c) 2005 Fabrice Bellard.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 typedef struct DVDSubContext
{
26 static int dvdsub_init_decoder(AVCodecContext
*avctx
)
31 static uint16_t getbe16(const uint8_t *p
)
33 return (p
[0] << 8) | p
[1];
36 static int get_nibble(const uint8_t *buf
, int nibble_offset
)
38 return (buf
[nibble_offset
>> 1] >> ((1 - (nibble_offset
& 1)) << 2)) & 0xf;
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
)
45 int x
, y
, len
, color
, nibble_end
;
48 nibble_end
= buf_size
* 2;
53 if (nibble_offset
>= nibble_end
)
55 v
= get_nibble(buf
, nibble_offset
++);
57 v
= (v
<< 4) | get_nibble(buf
, nibble_offset
++);
59 v
= (v
<< 4) | get_nibble(buf
, nibble_offset
++);
61 v
= (v
<< 4) | get_nibble(buf
, nibble_offset
++);
72 memset(d
+ x
, color
, len
);
81 nibble_offset
+= (nibble_offset
& 1);
87 static void guess_palette(uint32_t *rgba_palette
,
90 uint32_t subtitle_color
)
92 uint8_t color_used
[16];
93 int nb_opaque_colors
, i
, level
, j
, r
, g
, b
;
95 for(i
= 0; i
< 4; i
++)
98 memset(color_used
, 0, 16);
100 for(i
= 0; i
< 4; i
++) {
101 if (alpha
[i
] != 0 && !color_used
[palette
[i
]]) {
102 color_used
[palette
[i
]] = 1;
107 if (nb_opaque_colors
== 0)
110 j
= nb_opaque_colors
;
111 memset(color_used
, 0, 16);
112 for(i
= 0; i
< 4; i
++) {
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);
123 rgba_palette
[i
] = (rgba_palette
[color_used
[palette
[i
]] - 1] & 0x00ffffff) |
124 ((alpha
[i
] * 17) << 24);
130 static int decode_dvd_subtitles(AVSubtitle
*sub_header
,
131 const uint8_t *buf
, int buf_size
)
133 int cmd_pos
, pos
, cmd
, x1
, y1
, x2
, y2
, offset1
, offset2
, next_cmd_pos
;
134 uint8_t palette
[4], alpha
[4];
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;
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);
151 av_log(NULL
, AV_LOG_INFO
, "cmd_pos=0x%04x next=0x%04x date=%d\n",
152 cmd_pos
, next_cmd_pos
, date
);
157 x1
= y1
= x2
= y2
= 0;
158 while (pos
< buf_size
) {
161 av_log(NULL
, AV_LOG_INFO
, "cmd=%02x\n", cmd
);
165 /* menu subpicture */
170 sub_header
->start_display_time
= (date
<< 10) / 90;
174 sub_header
->end_display_time
= (date
<< 10) / 90;
178 if ((buf_size
- pos
) < 2)
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;
188 if ((buf_size
- pos
) < 2)
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;
196 av_log(NULL
, AV_LOG_INFO
, "alpha=%x%x%x%x\n", alpha
[0],alpha
[1],alpha
[2],alpha
[3]);
200 if ((buf_size
- pos
) < 6)
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];
207 av_log(NULL
, AV_LOG_INFO
, "x1=%d x2=%d y1=%d y2=%d\n",
213 if ((buf_size
- pos
) < 4)
215 offset1
= getbe16(buf
+ pos
);
216 offset2
= getbe16(buf
+ pos
+ 2);
218 av_log(NULL
, AV_LOG_INFO
, "offset1=0x%04x offset2=0x%04x\n", offset1
, offset2
);
232 /* decode the bitmap */
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
);
245 av_freep(&sub_header
->rects
);
246 sub_header
->num_rects
= 0;
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
;
268 if (next_cmd_pos
== cmd_pos
)
270 cmd_pos
= next_cmd_pos
;
272 if (sub_header
->num_rects
> 0)
278 static int is_transp(const uint8_t *buf
, int pitch
, int n
,
279 const uint8_t *transp_color
)
282 for(i
= 0; i
< n
; i
++) {
283 if (!transp_color
[*buf
])
290 /* return 0 if empty rectangle, 1 if non empty */
291 static int find_smallest_bounding_rectangle(AVSubtitle
*s
)
293 uint8_t transp_color
[256];
294 int y1
, y2
, x1
, x2
, y
, w
, h
, i
;
297 if (s
->num_rects
== 0 || s
->rects
== NULL
|| s
->rects
[0].w
<= 0 || s
->rects
[0].h
<= 0)
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)
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
))
309 if (y1
== s
->rects
[0].h
) {
310 av_freep(&s
->rects
[0].bitmap
);
311 s
->rects
[0].w
= s
->rects
[0].h
= 0;
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
))
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
))
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
,
329 bitmap
= av_malloc(w
* h
);
332 for(y
= 0; y
< h
; y
++) {
333 memcpy(bitmap
+ w
* y
, s
->rects
[0].bitmap
+ x1
+ (y1
+ y
) * s
->rects
[0].linesize
, w
);
335 av_freep(&s
->rects
[0].bitmap
);
336 s
->rects
[0].bitmap
= bitmap
;
337 s
->rects
[0].linesize
= w
;
345 static int dvdsub_close_decoder(AVCodecContext
*avctx
)
352 static void ppm_save(const char *filename
, uint8_t *bitmap
, int w
, int h
,
353 uint32_t *rgba_palette
)
358 f
= fopen(filename
, "w");
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
);
379 static int dvdsub_decode(AVCodecContext
*avctx
,
380 void *data
, int *data_size
,
381 uint8_t *buf
, int buf_size
)
383 AVSubtitle
*sub
= (void *)data
;
386 is_menu
= decode_dvd_subtitles(sub
, buf
, buf_size
);
394 if (!is_menu
&& find_smallest_bounding_rectangle(sub
) == 0)
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
);
409 AVCodec dvdsub_decoder
= {
412 CODEC_ID_DVD_SUBTITLE
,
413 sizeof(DVDSubContext
),
416 dvdsub_close_decoder
,
420 /* parser definition */
421 typedef struct DVDSubParseContext
{
425 } DVDSubParseContext
;
427 static int dvdsub_parse_init(AVCodecParserContext
*s
)
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
)
437 DVDSubParseContext
*pc
= s
->priv_data
;
439 if (pc
->packet_index
== 0) {
442 pc
->packet_len
= (buf
[0] << 8) | buf
[1];
443 av_freep(&pc
->packet
);
444 pc
->packet
= av_malloc(pc
->packet_len
);
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;
458 pc
->packet_index
= 0;
466 static void dvdsub_parse_close(AVCodecParserContext
*s
)
468 DVDSubParseContext
*pc
= s
->priv_data
;
469 av_freep(&pc
->packet
);
472 AVCodecParser dvdsub_parser
= {
473 { CODEC_ID_DVD_SUBTITLE
},
474 sizeof(DVDSubParseContext
),