2 * DVD subtitle encoding
3 * Copyright (c) 2005 Wolfram Gloger
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
22 #include "bytestream.h"
24 #include "libavutil/avassert.h"
25 #include "libavutil/bprint.h"
26 #include "libavutil/imgutils.h"
27 #include "libavutil/opt.h"
31 uint32_t global_palette
[16];
35 // ncnt is the nibble counter
36 #define PUTNIBBLE(val)\
39 *q++ = bitbuf | ((val) & 0x0f);\
44 static void dvd_encode_rle(uint8_t **pq
,
45 const uint8_t *bitmap
, int linesize
,
50 unsigned int bitbuf
= 0;
56 for (y
= 0; y
< h
; ++y
) {
58 for(x
= 0; x
< w
; x
+= len
) {
60 for (len
=1; x
+len
< w
; ++len
)
61 if (bitmap
[x
+len
] != color
)
64 av_assert0(color
< 4);
66 PUTNIBBLE((len
<< 2)|color
);
67 } else if (len
< 0x10) {
69 PUTNIBBLE((len
<< 2)|color
);
70 } else if (len
< 0x40) {
73 PUTNIBBLE((len
<< 2)|color
);
74 } else if (x
+len
== w
) {
85 PUTNIBBLE((len
<< 2)|color
);
97 static int color_distance(uint32_t a
, uint32_t b
)
100 int alpha_a
= 8, alpha_b
= 8;
102 for (i
= 24; i
>= 0; i
-= 8) {
103 d
= alpha_a
* (int)((a
>> i
) & 0xFF) -
104 alpha_b
* (int)((b
>> i
) & 0xFF);
113 * Count colors used in a rectangle, quantizing alpha and grouping by
114 * nearest global palette entry.
116 static void count_colors(AVCodecContext
*avctx
, unsigned hits
[33],
117 const AVSubtitleRect
*r
)
119 DVDSubtitleContext
*dvdc
= avctx
->priv_data
;
120 unsigned count
[256] = { 0 };
121 uint32_t *palette
= (uint32_t *)r
->pict
.data
[1];
123 int x
, y
, i
, j
, match
, d
, best_d
, av_uninit(best_j
);
124 uint8_t *p
= r
->pict
.data
[0];
126 for (y
= 0; y
< r
->h
; y
++) {
127 for (x
= 0; x
< r
->w
; x
++)
129 p
+= r
->pict
.linesize
[0] - r
->w
;
131 for (i
= 0; i
< 256; i
++) {
132 if (!count
[i
]) /* avoid useless search */
135 /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */
136 match
= color
< 0x33000000 ? 0 : color
< 0xCC000000 ? 1 : 17;
139 for (j
= 0; j
< 16; j
++) {
140 d
= color_distance(0xFF000000 | color
,
141 0xFF000000 | dvdc
->global_palette
[j
]);
149 hits
[match
] += count
[i
];
153 static void select_palette(AVCodecContext
*avctx
, int out_palette
[4],
154 int out_alpha
[4], unsigned hits
[33])
156 DVDSubtitleContext
*dvdc
= avctx
->priv_data
;
157 int i
, j
, bright
, mult
;
159 int selected
[4] = { 0 };
160 uint32_t pseudopal
[33] = { 0 };
161 uint32_t refcolor
[3] = { 0x00000000, 0xFFFFFFFF, 0xFF000000 };
163 /* Bonus for transparent: if the rectangle fits tightly the text, the
164 background color can be quite rare, but it would be ugly without it */
166 /* Bonus for bright colors */
167 for (i
= 0; i
< 16; i
++) {
168 if (!(hits
[1 + i
] + hits
[17 + i
]))
169 continue; /* skip unused colors to gain time */
170 color
= dvdc
->global_palette
[i
];
172 for (j
= 0; j
< 3; j
++, color
>>= 8)
173 bright
+= (color
& 0xFF) < 0x40 || (color
& 0xFF) >= 0xC0;
174 mult
= 2 + FFMIN(bright
, 2);
175 hits
[ 1 + i
] *= mult
;
176 hits
[17 + i
] *= mult
;
179 /* Select four most frequent colors */
180 for (i
= 0; i
< 4; i
++) {
181 for (j
= 0; j
< 33; j
++)
182 if (hits
[j
] > hits
[selected
[i
]])
184 hits
[selected
[i
]] = 0;
187 /* Order the colors like in most DVDs:
188 0: background, 1: foreground, 2: outline */
189 for (i
= 0; i
< 16; i
++) {
190 pseudopal
[ 1 + i
] = 0x80000000 | dvdc
->global_palette
[i
];
191 pseudopal
[17 + i
] = 0xFF000000 | dvdc
->global_palette
[i
];
193 for (i
= 0; i
< 3; i
++) {
194 int best_d
= color_distance(refcolor
[i
], pseudopal
[selected
[i
]]);
195 for (j
= i
+ 1; j
< 4; j
++) {
196 int d
= color_distance(refcolor
[i
], pseudopal
[selected
[j
]]);
198 FFSWAP(int, selected
[i
], selected
[j
]);
205 for (i
= 0; i
< 4; i
++) {
206 out_palette
[i
] = selected
[i
] ? (selected
[i
] - 1) & 0xF : 0;
207 out_alpha
[i
] = !selected
[i
] ? 0 : selected
[i
] < 17 ? 0x80 : 0xFF;
211 static void build_color_map(AVCodecContext
*avctx
, int cmap
[],
212 const uint32_t palette
[],
213 const int out_palette
[], unsigned int const out_alpha
[])
215 DVDSubtitleContext
*dvdc
= avctx
->priv_data
;
217 uint32_t pseudopal
[4];
219 for (i
= 0; i
< 4; i
++)
220 pseudopal
[i
] = (out_alpha
[i
] << 24) |
221 dvdc
->global_palette
[out_palette
[i
]];
222 for (i
= 0; i
< 256; i
++) {
224 for (j
= 0; j
< 4; j
++) {
225 d
= color_distance(pseudopal
[j
], palette
[i
]);
234 static void copy_rectangle(AVSubtitleRect
*dst
, AVSubtitleRect
*src
, int cmap
[])
239 p
= src
->pict
.data
[0];
240 q
= dst
->pict
.data
[0] + (src
->x
- dst
->x
) +
241 (src
->y
- dst
->y
) * dst
->pict
.linesize
[0];
242 for (y
= 0; y
< src
->h
; y
++) {
243 for (x
= 0; x
< src
->w
; x
++)
244 *(q
++) = cmap
[*(p
++)];
245 p
+= src
->pict
.linesize
[0] - src
->w
;
246 q
+= dst
->pict
.linesize
[0] - src
->w
;
250 static int encode_dvd_subtitles(AVCodecContext
*avctx
,
251 uint8_t *outbuf
, int outbuf_size
,
254 DVDSubtitleContext
*dvdc
= avctx
->priv_data
;
256 int offset1
, offset2
;
257 int i
, rects
= h
->num_rects
, ret
;
258 unsigned global_palette_hits
[33] = { 0 };
262 AVSubtitleRect vrect
;
263 uint8_t *vrect_data
= NULL
;
267 if (rects
== 0 || !h
->rects
)
268 return AVERROR(EINVAL
);
269 for (i
= 0; i
< rects
; i
++)
270 if (h
->rects
[i
]->type
!= SUBTITLE_BITMAP
) {
271 av_log(avctx
, AV_LOG_ERROR
, "Bitmap subtitle required\n");
272 return AVERROR(EINVAL
);
274 /* Mark this subtitle forced if any of the rectangles is forced. */
275 for (i
= 0; i
< rects
; i
++)
276 if ((h
->rects
[i
]->flags
& AV_SUBTITLE_FLAG_FORCED
) != 0) {
280 vrect
= *h
->rects
[0];
283 /* DVD subtitles can have only one rectangle: build a virtual
284 rectangle containing all actual rectangles.
285 The data of the rectangles will be copied later, when the palette
286 is decided, because the rectangles may have different palettes. */
287 int xmin
= h
->rects
[0]->x
, xmax
= xmin
+ h
->rects
[0]->w
;
288 int ymin
= h
->rects
[0]->y
, ymax
= ymin
+ h
->rects
[0]->h
;
289 for (i
= 1; i
< rects
; i
++) {
290 xmin
= FFMIN(xmin
, h
->rects
[i
]->x
);
291 ymin
= FFMIN(ymin
, h
->rects
[i
]->y
);
292 xmax
= FFMAX(xmax
, h
->rects
[i
]->x
+ h
->rects
[i
]->w
);
293 ymax
= FFMAX(ymax
, h
->rects
[i
]->y
+ h
->rects
[i
]->h
);
297 vrect
.w
= xmax
- xmin
;
298 vrect
.h
= ymax
- ymin
;
299 if ((ret
= av_image_check_size(vrect
.w
, vrect
.h
, 0, avctx
)) < 0)
302 /* Count pixels outside the virtual rectangle as transparent */
303 global_palette_hits
[0] = vrect
.w
* vrect
.h
;
304 for (i
= 0; i
< rects
; i
++)
305 global_palette_hits
[0] -= h
->rects
[i
]->w
* h
->rects
[i
]->h
;
308 for (i
= 0; i
< rects
; i
++)
309 count_colors(avctx
, global_palette_hits
, h
->rects
[i
]);
310 select_palette(avctx
, out_palette
, out_alpha
, global_palette_hits
);
313 if (!(vrect_data
= av_calloc(vrect
.w
, vrect
.h
)))
314 return AVERROR(ENOMEM
);
315 vrect
.pict
.data
[0] = vrect_data
;
316 vrect
.pict
.linesize
[0] = vrect
.w
;
317 for (i
= 0; i
< rects
; i
++) {
318 build_color_map(avctx
, cmap
, (uint32_t *)h
->rects
[i
]->pict
.data
[1],
319 out_palette
, out_alpha
);
320 copy_rectangle(&vrect
, h
->rects
[i
], cmap
);
322 for (i
= 0; i
< 4; i
++)
325 build_color_map(avctx
, cmap
, (uint32_t *)h
->rects
[0]->pict
.data
[1],
326 out_palette
, out_alpha
);
329 av_log(avctx
, AV_LOG_DEBUG
, "Selected palette:");
330 for (i
= 0; i
< 4; i
++)
331 av_log(avctx
, AV_LOG_DEBUG
, " 0x%06x@@%02x (0x%x,0x%x)",
332 dvdc
->global_palette
[out_palette
[i
]], out_alpha
[i
],
333 out_palette
[i
], out_alpha
[i
] >> 4);
334 av_log(avctx
, AV_LOG_DEBUG
, "\n");
338 offset1
= q
- outbuf
;
339 // worst case memory requirement: 1 nibble per pixel..
340 if ((q
- outbuf
) + vrect
.w
* vrect
.h
/ 2 + 17 + 21 > outbuf_size
) {
341 av_log(NULL
, AV_LOG_ERROR
, "dvd_subtitle too big\n");
342 ret
= AVERROR_BUFFER_TOO_SMALL
;
345 dvd_encode_rle(&q
, vrect
.pict
.data
[0], vrect
.w
* 2,
346 vrect
.w
, (vrect
.h
+ 1) >> 1, cmap
);
347 offset2
= q
- outbuf
;
348 dvd_encode_rle(&q
, vrect
.pict
.data
[0] + vrect
.w
, vrect
.w
* 2,
349 vrect
.w
, vrect
.h
>> 1, cmap
);
351 if (dvdc
->even_rows_fix
&& (vrect
.h
& 1)) {
352 // Work-around for some players that want the height to be even.
354 *q
++ = 0x00; // 0x00 0x00 == empty row, i.e. fully transparent
358 // set data packet size
360 bytestream_put_be16(&qq
, q
- outbuf
);
362 // send start display command
363 bytestream_put_be16(&q
, (h
->start_display_time
*90) >> 10);
364 bytestream_put_be16(&q
, (q
- outbuf
) /*- 2 */ + 8 + 12 + 2);
365 *q
++ = 0x03; // palette - 4 nibbles
366 *q
++ = (out_palette
[3] << 4) | out_palette
[2];
367 *q
++ = (out_palette
[1] << 4) | out_palette
[0];
368 *q
++ = 0x04; // alpha - 4 nibbles
369 *q
++ = (out_alpha
[3] & 0xF0) | (out_alpha
[2] >> 4);
370 *q
++ = (out_alpha
[1] & 0xF0) | (out_alpha
[0] >> 4);
373 x2
= vrect
.x
+ vrect
.w
- 1;
374 y2
= vrect
.y
+ vrect
.h
- 1;
377 // x1 x2 -> 6 nibbles
379 *q
++ = (vrect
.x
<< 4) | ((x2
>> 8) & 0xf);
381 // y1 y2 -> 6 nibbles
383 *q
++ = (vrect
.y
<< 4) | ((y2
>> 8) & 0xf);
388 bytestream_put_be16(&q
, offset1
);
389 bytestream_put_be16(&q
, offset2
);
391 *q
++ = forced
? 0x00 : 0x01; // start command
392 *q
++ = 0xff; // terminating command
394 // send stop display command last
395 bytestream_put_be16(&q
, (h
->end_display_time
*90) >> 10);
396 bytestream_put_be16(&q
, (q
- outbuf
) - 2 /*+ 4*/);
397 *q
++ = 0x02; // set end
398 *q
++ = 0xff; // terminating command
401 bytestream_put_be16(&qq
, q
- outbuf
);
403 av_log(NULL
, AV_LOG_DEBUG
, "subtitle_packet size=%"PTRDIFF_SPECIFIER
"\n", q
- outbuf
);
411 static int dvdsub_init(AVCodecContext
*avctx
)
413 DVDSubtitleContext
*dvdc
= avctx
->priv_data
;
414 static const uint32_t default_palette
[16] = {
415 0x000000, 0x0000FF, 0x00FF00, 0xFF0000,
416 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF,
417 0x808000, 0x8080FF, 0x800080, 0x80FF80,
418 0x008080, 0xFF8080, 0x555555, 0xAAAAAA,
423 av_assert0(sizeof(dvdc
->global_palette
) == sizeof(default_palette
));
424 memcpy(dvdc
->global_palette
, default_palette
, sizeof(dvdc
->global_palette
));
426 av_bprint_init(&extradata
, 0, 1);
427 if (avctx
->width
&& avctx
->height
)
428 av_bprintf(&extradata
, "size: %dx%d\n", avctx
->width
, avctx
->height
);
429 av_bprintf(&extradata
, "palette:");
430 for (i
= 0; i
< 16; i
++)
431 av_bprintf(&extradata
, " %06"PRIx32
"%c",
432 dvdc
->global_palette
[i
] & 0xFFFFFF, i
< 15 ? ',' : '\n');
434 ret
= avpriv_bprint_to_extradata(avctx
, &extradata
);
441 static int dvdsub_encode(AVCodecContext
*avctx
,
442 unsigned char *buf
, int buf_size
,
443 const AVSubtitle
*sub
)
445 //DVDSubtitleContext *s = avctx->priv_data;
448 ret
= encode_dvd_subtitles(avctx
, buf
, buf_size
, sub
);
452 #define OFFSET(x) offsetof(DVDSubtitleContext, x)
453 #define SE AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM
454 static const AVOption options
[] = {
455 {"even_rows_fix", "Make number of rows even (workaround for some players)", OFFSET(even_rows_fix
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, SE
},
459 static const AVClass dvdsubenc_class
= {
460 .class_name
= "VOBSUB subtitle encoder",
461 .item_name
= av_default_item_name
,
463 .version
= LIBAVUTIL_VERSION_INT
,
466 AVCodec ff_dvdsub_encoder
= {
468 .long_name
= NULL_IF_CONFIG_SMALL("DVD subtitles"),
469 .type
= AVMEDIA_TYPE_SUBTITLE
,
470 .id
= AV_CODEC_ID_DVD_SUBTITLE
,
472 .encode_sub
= dvdsub_encode
,
473 .priv_class
= &dvdsubenc_class
,
474 .priv_data_size
= sizeof(DVDSubtitleContext
),