| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * DVD subtitle encoding | ||
| 3 | * Copyright (c) 2005 Wolfram Gloger | ||
| 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 | #include "avcodec.h" | ||
| 22 | #include "bytestream.h" | ||
| 23 | #include "codec_internal.h" | ||
| 24 | #include "dvdsub.h" | ||
| 25 | #include "libavutil/avassert.h" | ||
| 26 | #include "libavutil/bprint.h" | ||
| 27 | #include "libavutil/imgutils.h" | ||
| 28 | #include "libavutil/mem.h" | ||
| 29 | #include "libavutil/opt.h" | ||
| 30 | |||
| 31 | typedef struct { | ||
| 32 | AVClass *class; | ||
| 33 | uint32_t global_palette[16]; | ||
| 34 | char *palette_str; | ||
| 35 | int even_rows_fix; | ||
| 36 | } DVDSubtitleContext; | ||
| 37 | |||
| 38 | // ncnt is the nibble counter | ||
| 39 | #define PUTNIBBLE(val)\ | ||
| 40 | do {\ | ||
| 41 | if (ncnt++ & 1)\ | ||
| 42 | *q++ = bitbuf | ((val) & 0x0f);\ | ||
| 43 | else\ | ||
| 44 | bitbuf = (val) << 4;\ | ||
| 45 | } while(0) | ||
| 46 | |||
| 47 | 86 | static void dvd_encode_rle(uint8_t **pq, | |
| 48 | const uint8_t *bitmap, int linesize, | ||
| 49 | int w, int h, | ||
| 50 | const int cmap[256]) | ||
| 51 | { | ||
| 52 | uint8_t *q; | ||
| 53 | 86 | unsigned int bitbuf = 0; | |
| 54 | int ncnt; | ||
| 55 | int x, y, len, color; | ||
| 56 | |||
| 57 | 86 | q = *pq; | |
| 58 | |||
| 59 |
2/2✓ Branch 0 taken 1563 times.
✓ Branch 1 taken 86 times.
|
1649 | for (y = 0; y < h; ++y) { |
| 60 | 1563 | ncnt = 0; | |
| 61 |
2/2✓ Branch 0 taken 118206 times.
✓ Branch 1 taken 1563 times.
|
119769 | for(x = 0; x < w; x += len) { |
| 62 | 118206 | color = bitmap[x]; | |
| 63 |
2/2✓ Branch 0 taken 471493 times.
✓ Branch 1 taken 1563 times.
|
473056 | for (len=1; x+len < w; ++len) |
| 64 |
2/2✓ Branch 0 taken 116643 times.
✓ Branch 1 taken 354850 times.
|
471493 | if (bitmap[x+len] != color) |
| 65 | 116643 | break; | |
| 66 | 118206 | color = cmap[color]; | |
| 67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 118206 times.
|
118206 | av_assert0(color < 4); |
| 68 |
2/2✓ Branch 0 taken 94474 times.
✓ Branch 1 taken 23732 times.
|
118206 | if (len < 0x04) { |
| 69 |
2/2✓ Branch 0 taken 46942 times.
✓ Branch 1 taken 47532 times.
|
94474 | PUTNIBBLE((len << 2)|color); |
| 70 |
2/2✓ Branch 0 taken 21030 times.
✓ Branch 1 taken 2702 times.
|
23732 | } else if (len < 0x10) { |
| 71 |
2/2✓ Branch 0 taken 10167 times.
✓ Branch 1 taken 10863 times.
|
21030 | PUTNIBBLE(len >> 2); |
| 72 |
2/2✓ Branch 0 taken 10863 times.
✓ Branch 1 taken 10167 times.
|
21030 | PUTNIBBLE((len << 2)|color); |
| 73 |
2/2✓ Branch 0 taken 1843 times.
✓ Branch 1 taken 859 times.
|
2702 | } else if (len < 0x40) { |
| 74 |
2/2✓ Branch 0 taken 847 times.
✓ Branch 1 taken 996 times.
|
1843 | PUTNIBBLE(0); |
| 75 |
2/2✓ Branch 0 taken 996 times.
✓ Branch 1 taken 847 times.
|
1843 | PUTNIBBLE(len >> 2); |
| 76 |
2/2✓ Branch 0 taken 847 times.
✓ Branch 1 taken 996 times.
|
1843 | PUTNIBBLE((len << 2)|color); |
| 77 |
2/2✓ Branch 0 taken 321 times.
✓ Branch 1 taken 538 times.
|
859 | } else if (x+len == w) { |
| 78 |
2/2✓ Branch 0 taken 116 times.
✓ Branch 1 taken 205 times.
|
321 | PUTNIBBLE(0); |
| 79 |
2/2✓ Branch 0 taken 205 times.
✓ Branch 1 taken 116 times.
|
321 | PUTNIBBLE(0); |
| 80 |
2/2✓ Branch 0 taken 116 times.
✓ Branch 1 taken 205 times.
|
321 | PUTNIBBLE(0); |
| 81 |
2/2✓ Branch 0 taken 205 times.
✓ Branch 1 taken 116 times.
|
321 | PUTNIBBLE(color); |
| 82 | } else { | ||
| 83 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 512 times.
|
538 | if (len > 0xff) |
| 84 | 26 | len = 0xff; | |
| 85 |
2/2✓ Branch 0 taken 147 times.
✓ Branch 1 taken 391 times.
|
538 | PUTNIBBLE(0); |
| 86 |
2/2✓ Branch 0 taken 391 times.
✓ Branch 1 taken 147 times.
|
538 | PUTNIBBLE(len >> 6); |
| 87 |
2/2✓ Branch 0 taken 147 times.
✓ Branch 1 taken 391 times.
|
538 | PUTNIBBLE(len >> 2); |
| 88 |
2/2✓ Branch 0 taken 391 times.
✓ Branch 1 taken 147 times.
|
538 | PUTNIBBLE((len << 2)|color); |
| 89 | } | ||
| 90 | } | ||
| 91 | /* end of line */ | ||
| 92 |
2/2✓ Branch 0 taken 739 times.
✓ Branch 1 taken 824 times.
|
1563 | if (ncnt & 1) |
| 93 |
1/2✓ Branch 0 taken 739 times.
✗ Branch 1 not taken.
|
739 | PUTNIBBLE(0); |
| 94 | 1563 | bitmap += linesize; | |
| 95 | } | ||
| 96 | |||
| 97 | 86 | *pq = q; | |
| 98 | 86 | } | |
| 99 | |||
| 100 | 45795 | static int color_distance(uint32_t a, uint32_t b) | |
| 101 | { | ||
| 102 | 45795 | int r = 0, d, i; | |
| 103 | 45795 | int alpha_a = 8, alpha_b = 8; | |
| 104 | |||
| 105 |
2/2✓ Branch 0 taken 183180 times.
✓ Branch 1 taken 45795 times.
|
228975 | for (i = 24; i >= 0; i -= 8) { |
| 106 | 183180 | d = alpha_a * (int)((a >> i) & 0xFF) - | |
| 107 | 183180 | alpha_b * (int)((b >> i) & 0xFF); | |
| 108 | 183180 | r += d * d; | |
| 109 | 183180 | alpha_a = a >> 28; | |
| 110 | 183180 | alpha_b = b >> 28; | |
| 111 | } | ||
| 112 | 45795 | return r; | |
| 113 | } | ||
| 114 | |||
| 115 | /** | ||
| 116 | * Count colors used in a rectangle, quantizing alpha and grouping by | ||
| 117 | * nearest global palette entry. | ||
| 118 | */ | ||
| 119 | 43 | static void count_colors(AVCodecContext *avctx, unsigned hits[33], | |
| 120 | const AVSubtitleRect *r) | ||
| 121 | { | ||
| 122 | 43 | DVDSubtitleContext *dvdc = avctx->priv_data; | |
| 123 | 43 | unsigned count[256] = { 0 }; | |
| 124 | 43 | uint32_t *palette = (uint32_t *)r->data[1]; | |
| 125 | uint32_t color; | ||
| 126 | 43 | int x, y, i, j, match, d, best_d, av_uninit(best_j); | |
| 127 | 43 | uint8_t *p = r->data[0]; | |
| 128 | |||
| 129 |
2/2✓ Branch 0 taken 1563 times.
✓ Branch 1 taken 43 times.
|
1606 | for (y = 0; y < r->h; y++) { |
| 130 |
2/2✓ Branch 0 taken 472147 times.
✓ Branch 1 taken 1563 times.
|
473710 | for (x = 0; x < r->w; x++) |
| 131 | 472147 | count[*(p++)]++; | |
| 132 | 1563 | p += r->linesize[0] - r->w; | |
| 133 | } | ||
| 134 |
2/2✓ Branch 0 taken 11008 times.
✓ Branch 1 taken 43 times.
|
11051 | for (i = 0; i < 256; i++) { |
| 135 |
2/2✓ Branch 0 taken 10836 times.
✓ Branch 1 taken 172 times.
|
11008 | if (!count[i]) /* avoid useless search */ |
| 136 | 10836 | continue; | |
| 137 | 172 | color = palette[i]; | |
| 138 | /* 0: transparent, 1-16: semi-transparent, 17-33 opaque */ | ||
| 139 |
3/4✓ Branch 0 taken 86 times.
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 86 times.
|
172 | match = color < 0x33000000 ? 0 : color < 0xCC000000 ? 1 : 17; |
| 140 |
2/2✓ Branch 0 taken 86 times.
✓ Branch 1 taken 86 times.
|
172 | if (match) { |
| 141 | 86 | best_d = INT_MAX; | |
| 142 |
2/2✓ Branch 0 taken 1376 times.
✓ Branch 1 taken 86 times.
|
1462 | for (j = 0; j < 16; j++) { |
| 143 | 1376 | d = color_distance(0xFF000000 | color, | |
| 144 | 1376 | 0xFF000000 | dvdc->global_palette[j]); | |
| 145 |
2/2✓ Branch 0 taken 172 times.
✓ Branch 1 taken 1204 times.
|
1376 | if (d < best_d) { |
| 146 | 172 | best_d = d; | |
| 147 | 172 | best_j = j; | |
| 148 | } | ||
| 149 | } | ||
| 150 | 86 | match += best_j; | |
| 151 | } | ||
| 152 | 172 | hits[match] += count[i]; | |
| 153 | } | ||
| 154 | 43 | } | |
| 155 | |||
| 156 | 43 | static void select_palette(AVCodecContext *avctx, int out_palette[4], | |
| 157 | int out_alpha[4], unsigned hits[33]) | ||
| 158 | { | ||
| 159 | 43 | DVDSubtitleContext *dvdc = avctx->priv_data; | |
| 160 | int i, j, bright, mult; | ||
| 161 | uint32_t color; | ||
| 162 | 43 | int selected[4] = { 0 }; | |
| 163 | 43 | uint32_t pseudopal[33] = { 0 }; | |
| 164 | 43 | uint32_t refcolor[3] = { 0x00000000, 0xFFFFFFFF, 0xFF000000 }; | |
| 165 | |||
| 166 | /* Bonus for transparent: if the rectangle fits tightly the text, the | ||
| 167 | background color can be quite rare, but it would be ugly without it */ | ||
| 168 | 43 | hits[0] *= 16; | |
| 169 | /* Bonus for bright colors */ | ||
| 170 |
2/2✓ Branch 0 taken 688 times.
✓ Branch 1 taken 43 times.
|
731 | for (i = 0; i < 16; i++) { |
| 171 |
2/2✓ Branch 0 taken 602 times.
✓ Branch 1 taken 86 times.
|
688 | if (!(hits[1 + i] + hits[17 + i])) |
| 172 | 602 | continue; /* skip unused colors to gain time */ | |
| 173 | 86 | color = dvdc->global_palette[i]; | |
| 174 | 86 | bright = 0; | |
| 175 |
2/2✓ Branch 0 taken 258 times.
✓ Branch 1 taken 86 times.
|
344 | for (j = 0; j < 3; j++, color >>= 8) |
| 176 |
3/4✓ Branch 0 taken 86 times.
✓ Branch 1 taken 172 times.
✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
|
258 | bright += (color & 0xFF) < 0x40 || (color & 0xFF) >= 0xC0; |
| 177 | 86 | mult = 2 + FFMIN(bright, 2); | |
| 178 | 86 | hits[ 1 + i] *= mult; | |
| 179 | 86 | hits[17 + i] *= mult; | |
| 180 | } | ||
| 181 | |||
| 182 | /* Select four most frequent colors */ | ||
| 183 |
2/2✓ Branch 0 taken 172 times.
✓ Branch 1 taken 43 times.
|
215 | for (i = 0; i < 4; i++) { |
| 184 |
2/2✓ Branch 0 taken 5676 times.
✓ Branch 1 taken 172 times.
|
5848 | for (j = 0; j < 33; j++) |
| 185 |
2/2✓ Branch 0 taken 129 times.
✓ Branch 1 taken 5547 times.
|
5676 | if (hits[j] > hits[selected[i]]) |
| 186 | 129 | selected[i] = j; | |
| 187 | 172 | hits[selected[i]] = 0; | |
| 188 | } | ||
| 189 | |||
| 190 | /* Order the colors like in most DVDs: | ||
| 191 | 0: background, 1: foreground, 2: outline */ | ||
| 192 |
2/2✓ Branch 0 taken 688 times.
✓ Branch 1 taken 43 times.
|
731 | for (i = 0; i < 16; i++) { |
| 193 | 688 | pseudopal[ 1 + i] = 0x80000000 | dvdc->global_palette[i]; | |
| 194 | 688 | pseudopal[17 + i] = 0xFF000000 | dvdc->global_palette[i]; | |
| 195 | } | ||
| 196 |
2/2✓ Branch 0 taken 129 times.
✓ Branch 1 taken 43 times.
|
172 | for (i = 0; i < 3; i++) { |
| 197 | 129 | int best_d = color_distance(refcolor[i], pseudopal[selected[i]]); | |
| 198 |
2/2✓ Branch 0 taken 258 times.
✓ Branch 1 taken 129 times.
|
387 | for (j = i + 1; j < 4; j++) { |
| 199 | 258 | int d = color_distance(refcolor[i], pseudopal[selected[j]]); | |
| 200 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 258 times.
|
258 | if (d < best_d) { |
| 201 | ✗ | FFSWAP(int, selected[i], selected[j]); | |
| 202 | ✗ | best_d = d; | |
| 203 | } | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | /* Output */ | ||
| 208 |
2/2✓ Branch 0 taken 172 times.
✓ Branch 1 taken 43 times.
|
215 | for (i = 0; i < 4; i++) { |
| 209 |
2/2✓ Branch 0 taken 86 times.
✓ Branch 1 taken 86 times.
|
172 | out_palette[i] = selected[i] ? (selected[i] - 1) & 0xF : 0; |
| 210 |
3/4✓ Branch 0 taken 86 times.
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 86 times.
|
172 | out_alpha [i] = !selected[i] ? 0 : selected[i] < 17 ? 0x80 : 0xFF; |
| 211 | } | ||
| 212 | 43 | } | |
| 213 | |||
| 214 | 43 | static void build_color_map(AVCodecContext *avctx, int cmap[], | |
| 215 | const uint32_t palette[], | ||
| 216 | const int out_palette[], unsigned int const out_alpha[]) | ||
| 217 | { | ||
| 218 | 43 | DVDSubtitleContext *dvdc = avctx->priv_data; | |
| 219 | int i, j, d, best_d; | ||
| 220 | uint32_t pseudopal[4]; | ||
| 221 | |||
| 222 |
2/2✓ Branch 0 taken 172 times.
✓ Branch 1 taken 43 times.
|
215 | for (i = 0; i < 4; i++) |
| 223 | 172 | pseudopal[i] = (out_alpha[i] << 24) | | |
| 224 | 172 | dvdc->global_palette[out_palette[i]]; | |
| 225 |
2/2✓ Branch 0 taken 11008 times.
✓ Branch 1 taken 43 times.
|
11051 | for (i = 0; i < 256; i++) { |
| 226 | 11008 | best_d = INT_MAX; | |
| 227 |
2/2✓ Branch 0 taken 44032 times.
✓ Branch 1 taken 11008 times.
|
55040 | for (j = 0; j < 4; j++) { |
| 228 | 44032 | d = color_distance(pseudopal[j], palette[i]); | |
| 229 |
2/2✓ Branch 0 taken 11094 times.
✓ Branch 1 taken 32938 times.
|
44032 | if (d < best_d) { |
| 230 | 11094 | cmap[i] = j; | |
| 231 | 11094 | best_d = d; | |
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | 43 | } | |
| 236 | |||
| 237 | ✗ | static void copy_rectangle(AVSubtitleRect *dst, AVSubtitleRect *src, int cmap[]) | |
| 238 | { | ||
| 239 | int x, y; | ||
| 240 | uint8_t *p, *q; | ||
| 241 | |||
| 242 | ✗ | p = src->data[0]; | |
| 243 | ✗ | q = dst->data[0] + (src->x - dst->x) + | |
| 244 | ✗ | (src->y - dst->y) * dst->linesize[0]; | |
| 245 | ✗ | for (y = 0; y < src->h; y++) { | |
| 246 | ✗ | for (x = 0; x < src->w; x++) | |
| 247 | ✗ | *(q++) = cmap[*(p++)]; | |
| 248 | ✗ | p += src->linesize[0] - src->w; | |
| 249 | ✗ | q += dst->linesize[0] - src->w; | |
| 250 | } | ||
| 251 | ✗ | } | |
| 252 | |||
| 253 | 43 | static int dvdsub_encode(AVCodecContext *avctx, | |
| 254 | uint8_t *outbuf, int outbuf_size, | ||
| 255 | const AVSubtitle *h) | ||
| 256 | { | ||
| 257 | 43 | DVDSubtitleContext *dvdc = avctx->priv_data; | |
| 258 | uint8_t *q, *qq; | ||
| 259 | int offset1, offset2; | ||
| 260 | 43 | int i, rects = h->num_rects, ret; | |
| 261 | 43 | unsigned global_palette_hits[33] = { 0 }; | |
| 262 | int cmap[256]; | ||
| 263 | int out_palette[4]; | ||
| 264 | int out_alpha[4]; | ||
| 265 | AVSubtitleRect vrect; | ||
| 266 | 43 | uint8_t *vrect_data = NULL; | |
| 267 | int x2, y2; | ||
| 268 | 43 | int forced = 0; | |
| 269 | |||
| 270 |
2/4✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 43 times.
|
43 | if (rects == 0 || !h->rects) |
| 271 | ✗ | return AVERROR(EINVAL); | |
| 272 |
2/2✓ Branch 0 taken 43 times.
✓ Branch 1 taken 43 times.
|
86 | for (i = 0; i < rects; i++) |
| 273 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
|
43 | if (h->rects[i]->type != SUBTITLE_BITMAP) { |
| 274 | ✗ | av_log(avctx, AV_LOG_ERROR, "Bitmap subtitle required\n"); | |
| 275 | ✗ | return AVERROR(EINVAL); | |
| 276 | } | ||
| 277 | /* Mark this subtitle forced if any of the rectangles is forced. */ | ||
| 278 |
2/2✓ Branch 0 taken 43 times.
✓ Branch 1 taken 43 times.
|
86 | for (i = 0; i < rects; i++) |
| 279 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
|
43 | if ((h->rects[i]->flags & AV_SUBTITLE_FLAG_FORCED) != 0) { |
| 280 | ✗ | forced = 1; | |
| 281 | ✗ | break; | |
| 282 | } | ||
| 283 | |||
| 284 | 43 | vrect = *h->rects[0]; | |
| 285 | |||
| 286 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
|
43 | if (rects > 1) { |
| 287 | /* DVD subtitles can have only one rectangle: build a virtual | ||
| 288 | rectangle containing all actual rectangles. | ||
| 289 | The data of the rectangles will be copied later, when the palette | ||
| 290 | is decided, because the rectangles may have different palettes. */ | ||
| 291 | ✗ | int xmin = h->rects[0]->x, xmax = xmin + h->rects[0]->w; | |
| 292 | ✗ | int ymin = h->rects[0]->y, ymax = ymin + h->rects[0]->h; | |
| 293 | ✗ | for (i = 1; i < rects; i++) { | |
| 294 | ✗ | xmin = FFMIN(xmin, h->rects[i]->x); | |
| 295 | ✗ | ymin = FFMIN(ymin, h->rects[i]->y); | |
| 296 | ✗ | xmax = FFMAX(xmax, h->rects[i]->x + h->rects[i]->w); | |
| 297 | ✗ | ymax = FFMAX(ymax, h->rects[i]->y + h->rects[i]->h); | |
| 298 | } | ||
| 299 | ✗ | vrect.x = xmin; | |
| 300 | ✗ | vrect.y = ymin; | |
| 301 | ✗ | vrect.w = xmax - xmin; | |
| 302 | ✗ | vrect.h = ymax - ymin; | |
| 303 | ✗ | if ((ret = av_image_check_size(vrect.w, vrect.h, 0, avctx)) < 0) | |
| 304 | ✗ | return ret; | |
| 305 | |||
| 306 | /* Count pixels outside the virtual rectangle as transparent */ | ||
| 307 | ✗ | global_palette_hits[0] = vrect.w * vrect.h; | |
| 308 | ✗ | for (i = 0; i < rects; i++) | |
| 309 | ✗ | global_palette_hits[0] -= h->rects[i]->w * h->rects[i]->h; | |
| 310 | } | ||
| 311 | |||
| 312 |
2/2✓ Branch 0 taken 43 times.
✓ Branch 1 taken 43 times.
|
86 | for (i = 0; i < rects; i++) |
| 313 | 43 | count_colors(avctx, global_palette_hits, h->rects[i]); | |
| 314 | 43 | select_palette(avctx, out_palette, out_alpha, global_palette_hits); | |
| 315 | |||
| 316 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
|
43 | if (rects > 1) { |
| 317 | ✗ | if (!(vrect_data = av_calloc(vrect.w, vrect.h))) | |
| 318 | ✗ | return AVERROR(ENOMEM); | |
| 319 | ✗ | vrect.data [0] = vrect_data; | |
| 320 | ✗ | vrect.linesize[0] = vrect.w; | |
| 321 | ✗ | for (i = 0; i < rects; i++) { | |
| 322 | ✗ | build_color_map(avctx, cmap, (uint32_t *)h->rects[i]->data[1], | |
| 323 | out_palette, out_alpha); | ||
| 324 | ✗ | copy_rectangle(&vrect, h->rects[i], cmap); | |
| 325 | } | ||
| 326 | ✗ | for (i = 0; i < 4; i++) | |
| 327 | ✗ | cmap[i] = i; | |
| 328 | } else { | ||
| 329 | 43 | build_color_map(avctx, cmap, (uint32_t *)h->rects[0]->data[1], | |
| 330 | out_palette, out_alpha); | ||
| 331 | } | ||
| 332 | |||
| 333 | 43 | av_log(avctx, AV_LOG_DEBUG, "Selected palette:"); | |
| 334 |
2/2✓ Branch 0 taken 172 times.
✓ Branch 1 taken 43 times.
|
215 | for (i = 0; i < 4; i++) |
| 335 | 172 | av_log(avctx, AV_LOG_DEBUG, " 0x%06"PRIx32"@@%02x (0x%x,0x%x)", | |
| 336 | 172 | dvdc->global_palette[out_palette[i]], out_alpha[i], | |
| 337 | 172 | out_palette[i], out_alpha[i] >> 4); | |
| 338 | 43 | av_log(avctx, AV_LOG_DEBUG, "\n"); | |
| 339 | |||
| 340 | // encode data block | ||
| 341 | 43 | q = outbuf + 4; | |
| 342 | 43 | offset1 = q - outbuf; | |
| 343 | // worst case memory requirement: 1 nibble per pixel.. | ||
| 344 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
|
43 | if ((q - outbuf) + vrect.w * vrect.h / 2 + 17 + 21 > outbuf_size) { |
| 345 | ✗ | av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n"); | |
| 346 | ✗ | ret = AVERROR_BUFFER_TOO_SMALL; | |
| 347 | ✗ | goto fail; | |
| 348 | } | ||
| 349 | 43 | dvd_encode_rle(&q, vrect.data[0], vrect.w * 2, | |
| 350 | 43 | vrect.w, (vrect.h + 1) >> 1, cmap); | |
| 351 | 43 | offset2 = q - outbuf; | |
| 352 | 43 | dvd_encode_rle(&q, vrect.data[0] + vrect.w, vrect.w * 2, | |
| 353 | 43 | vrect.w, vrect.h >> 1, cmap); | |
| 354 | |||
| 355 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
43 | if (dvdc->even_rows_fix && (vrect.h & 1)) { |
| 356 | // Work-around for some players that want the height to be even. | ||
| 357 | ✗ | vrect.h++; | |
| 358 | ✗ | *q++ = 0x00; // 0x00 0x00 == empty row, i.e. fully transparent | |
| 359 | ✗ | *q++ = 0x00; | |
| 360 | } | ||
| 361 | |||
| 362 | // set data packet size | ||
| 363 | 43 | qq = outbuf + 2; | |
| 364 | 43 | bytestream_put_be16(&qq, q - outbuf); | |
| 365 | |||
| 366 | // send start display command | ||
| 367 | 43 | bytestream_put_be16(&q, (h->start_display_time*90) >> 10); | |
| 368 | 43 | bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12 + 2); | |
| 369 | 43 | *q++ = 0x03; // palette - 4 nibbles | |
| 370 | 43 | *q++ = (out_palette[3] << 4) | out_palette[2]; | |
| 371 | 43 | *q++ = (out_palette[1] << 4) | out_palette[0]; | |
| 372 | 43 | *q++ = 0x04; // alpha - 4 nibbles | |
| 373 | 43 | *q++ = (out_alpha[3] & 0xF0) | (out_alpha[2] >> 4); | |
| 374 | 43 | *q++ = (out_alpha[1] & 0xF0) | (out_alpha[0] >> 4); | |
| 375 | |||
| 376 | // 12 bytes per rect | ||
| 377 | 43 | x2 = vrect.x + vrect.w - 1; | |
| 378 | 43 | y2 = vrect.y + vrect.h - 1; | |
| 379 | |||
| 380 |
2/4✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
|
43 | if ((avctx->width > 0 && x2 > avctx->width) || |
| 381 |
2/4✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 43 times.
|
43 | (avctx->height > 0 && y2 > avctx->height)) { |
| 382 | ✗ | av_log(avctx, AV_LOG_ERROR, "canvas_size(%d:%d) is too small(%d:%d) for render\n", | |
| 383 | avctx->width, avctx->height, x2, y2); | ||
| 384 | ✗ | ret = AVERROR(EINVAL); | |
| 385 | ✗ | goto fail; | |
| 386 | } | ||
| 387 | 43 | *q++ = 0x05; | |
| 388 | // x1 x2 -> 6 nibbles | ||
| 389 | 43 | *q++ = vrect.x >> 4; | |
| 390 | 43 | *q++ = (vrect.x << 4) | ((x2 >> 8) & 0xf); | |
| 391 | 43 | *q++ = x2; | |
| 392 | // y1 y2 -> 6 nibbles | ||
| 393 | 43 | *q++ = vrect.y >> 4; | |
| 394 | 43 | *q++ = (vrect.y << 4) | ((y2 >> 8) & 0xf); | |
| 395 | 43 | *q++ = y2; | |
| 396 | |||
| 397 | 43 | *q++ = 0x06; | |
| 398 | // offset1, offset2 | ||
| 399 | 43 | bytestream_put_be16(&q, offset1); | |
| 400 | 43 | bytestream_put_be16(&q, offset2); | |
| 401 | |||
| 402 | 43 | *q++ = forced ? 0x00 : 0x01; // start command | |
| 403 | 43 | *q++ = 0xff; // terminating command | |
| 404 | |||
| 405 | // send stop display command last | ||
| 406 | 43 | bytestream_put_be16(&q, (h->end_display_time*90) >> 10); | |
| 407 | 43 | bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/); | |
| 408 | 43 | *q++ = 0x02; // set end | |
| 409 | 43 | *q++ = 0xff; // terminating command | |
| 410 | |||
| 411 | 43 | qq = outbuf; | |
| 412 | 43 | bytestream_put_be16(&qq, q - outbuf); | |
| 413 | |||
| 414 | 43 | av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%td\n", q - outbuf); | |
| 415 | 43 | ret = q - outbuf; | |
| 416 | |||
| 417 | 43 | fail: | |
| 418 | 43 | av_free(vrect_data); | |
| 419 | 43 | return ret; | |
| 420 | } | ||
| 421 | |||
| 422 | 1 | static int bprint_to_extradata(AVCodecContext *avctx, struct AVBPrint *buf) | |
| 423 | { | ||
| 424 | int ret; | ||
| 425 | char *str; | ||
| 426 | |||
| 427 | 1 | ret = av_bprint_finalize(buf, &str); | |
| 428 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (ret < 0) |
| 429 | ✗ | return ret; | |
| 430 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (!av_bprint_is_complete(buf)) { |
| 431 | ✗ | av_free(str); | |
| 432 | ✗ | return AVERROR(ENOMEM); | |
| 433 | } | ||
| 434 | |||
| 435 | 1 | avctx->extradata = str; | |
| 436 | /* Note: the string is NUL terminated (so extradata can be read as a | ||
| 437 | * string), but the ending character is not accounted in the size (in | ||
| 438 | * binary formats you are likely not supposed to mux that character). When | ||
| 439 | * extradata is copied, it is also padded with AV_INPUT_BUFFER_PADDING_SIZE | ||
| 440 | * zeros. */ | ||
| 441 | 1 | avctx->extradata_size = buf->len; | |
| 442 | 1 | return 0; | |
| 443 | } | ||
| 444 | |||
| 445 | 1 | static av_cold int dvdsub_init(AVCodecContext *avctx) | |
| 446 | { | ||
| 447 | 1 | DVDSubtitleContext *dvdc = avctx->priv_data; | |
| 448 | static const uint32_t default_palette[16] = { | ||
| 449 | 0x000000, 0x0000FF, 0x00FF00, 0xFF0000, | ||
| 450 | 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF, | ||
| 451 | 0x808000, 0x8080FF, 0x800080, 0x80FF80, | ||
| 452 | 0x008080, 0xFF8080, 0x555555, 0xAAAAAA, | ||
| 453 | }; | ||
| 454 | AVBPrint extradata; | ||
| 455 | int i, ret; | ||
| 456 | |||
| 457 | av_assert0(sizeof(dvdc->global_palette) == sizeof(default_palette)); | ||
| 458 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (dvdc->palette_str) { |
| 459 | ✗ | ff_dvdsub_parse_palette(dvdc->global_palette, dvdc->palette_str); | |
| 460 | } else { | ||
| 461 | 1 | memcpy(dvdc->global_palette, default_palette, sizeof(dvdc->global_palette)); | |
| 462 | } | ||
| 463 | |||
| 464 | 1 | av_bprint_init(&extradata, 0, AV_BPRINT_SIZE_AUTOMATIC); | |
| 465 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | if (avctx->width && avctx->height) |
| 466 | 1 | av_bprintf(&extradata, "size: %dx%d\n", avctx->width, avctx->height); | |
| 467 | 1 | av_bprintf(&extradata, "palette:"); | |
| 468 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
|
17 | for (i = 0; i < 16; i++) |
| 469 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
|
16 | av_bprintf(&extradata, " %06"PRIx32"%c", |
| 470 | 16 | dvdc->global_palette[i] & 0xFFFFFF, i < 15 ? ',' : '\n'); | |
| 471 | |||
| 472 | 1 | ret = bprint_to_extradata(avctx, &extradata); | |
| 473 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (ret < 0) |
| 474 | ✗ | return ret; | |
| 475 | |||
| 476 | 1 | return 0; | |
| 477 | } | ||
| 478 | |||
| 479 | #define OFFSET(x) offsetof(DVDSubtitleContext, x) | ||
| 480 | #define SE AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM | ||
| 481 | static const AVOption options[] = { | ||
| 482 | {"palette", "set the global palette", OFFSET(palette_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SE }, | ||
| 483 | {"even_rows_fix", "Make number of rows even (workaround for some players)", OFFSET(even_rows_fix), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, SE}, | ||
| 484 | { NULL }, | ||
| 485 | }; | ||
| 486 | |||
| 487 | static const AVClass dvdsubenc_class = { | ||
| 488 | .class_name = "VOBSUB subtitle encoder", | ||
| 489 | .item_name = av_default_item_name, | ||
| 490 | .option = options, | ||
| 491 | .version = LIBAVUTIL_VERSION_INT, | ||
| 492 | }; | ||
| 493 | |||
| 494 | const FFCodec ff_dvdsub_encoder = { | ||
| 495 | .p.name = "dvdsub", | ||
| 496 | CODEC_LONG_NAME("DVD subtitles"), | ||
| 497 | .p.type = AVMEDIA_TYPE_SUBTITLE, | ||
| 498 | .p.id = AV_CODEC_ID_DVD_SUBTITLE, | ||
| 499 | .init = dvdsub_init, | ||
| 500 | FF_CODEC_ENCODE_SUB_CB(dvdsub_encode), | ||
| 501 | .p.priv_class = &dvdsubenc_class, | ||
| 502 | .priv_data_size = sizeof(DVDSubtitleContext), | ||
| 503 | }; | ||
| 504 |