| File: | spuunmux.c |
| Location: | line 402, column 17 |
| Description: | Potential leak of memory pointed to by 'newspu' |
| 1 | /* | |||
| 2 | spuunmux mainline | |||
| 3 | */ | |||
| 4 | /* | |||
| 5 | * Copyright (C) 2002, 2003 Jan Panteltje <panteltje@yahoo.com>, | |||
| 6 | * | |||
| 7 | * Modified by Zachary Brewster-Geisz, 2003, to work on big-endian | |||
| 8 | * machines. | |||
| 9 | * | |||
| 10 | * Modified by Henry Mason, 2003, to use both PNG and BMP, and to use | |||
| 11 | * the dvdauthor submux format. | |||
| 12 | * | |||
| 13 | * Modified and copy right Jan Panteltje 2002 | |||
| 14 | * This program is free software; you can redistribute it and/or modify | |||
| 15 | * it under the terms of the GNU General Public License as published by | |||
| 16 | * the Free Software Foundation; either version 2 of the License, or (at | |||
| 17 | * your option) any later version. | |||
| 18 | * | |||
| 19 | * With many changes by Scott Smith (trckjunky@users.sourceforge.net) | |||
| 20 | * | |||
| 21 | * Svcd decoding by Giacomo Comes <encode2mpeg@users.sourceforge.net> | |||
| 22 | * | |||
| 23 | * This program is distributed in the hope that it will be useful, but | |||
| 24 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| 26 | * General Public License for more details. | |||
| 27 | * | |||
| 28 | * You should have received a copy of the GNU General Public License | |||
| 29 | * along with this program; if not, write to the Free Software | |||
| 30 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
| 31 | * MA 02110-1301 USA. | |||
| 32 | */ | |||
| 33 | ||||
| 34 | #include "config.h" | |||
| 35 | #include "compat.h" | |||
| 36 | ||||
| 37 | #include <fcntl.h> | |||
| 38 | #include <errno(*__errno_location ()).h> | |||
| 39 | ||||
| 40 | #include <netinet/in.h> | |||
| 41 | ||||
| 42 | #include <png.h> | |||
| 43 | #include <zlib.h> | |||
| 44 | ||||
| 45 | #include "rgb.h" | |||
| 46 | #include "common.h" | |||
| 47 | #include "conffile.h" | |||
| 48 | ||||
| 49 | #define CBUFSIZE65536 65536 /* big enough for any MPEG packet */ | |||
| 50 | #define PSBUFSIZE10 10 | |||
| 51 | ||||
| 52 | static unsigned int add_offset; | |||
| 53 | ||||
| 54 | static int debug = 0; | |||
| 55 | ||||
| 56 | static int video_format = VF_NONE; | |||
| 57 | static bool_Bool full_size = false0; | |||
| 58 | static unsigned int pts, spts, subi, subs, subno; | |||
| 59 | static int ofs, ofs1; | |||
| 60 | /* offsets from beginning of SPU to bottom and top field data set by last SPU_SET_DSPXA command */ | |||
| 61 | static unsigned char sub[65536]; | |||
| 62 | static unsigned char next_bits; | |||
| 63 | static const char *base_name; | |||
| 64 | static unsigned int have_bits; | |||
| 65 | static FILE *fdo; | |||
| 66 | static unsigned char svcd_adjust; | |||
| 67 | ||||
| 68 | static colorspec | |||
| 69 | current_palette[16]; /* current PGC colour table, alpha unused */ | |||
| 70 | ||||
| 71 | struct spu /* data for one subpicture unit (SPU) */ | |||
| 72 | { | |||
| 73 | unsigned char *img; /* image data */ | |||
| 74 | unsigned int x0, y0, xd, yd; /* display bounds */ | |||
| 75 | unsigned int pts[2]; /* start time, end time */ | |||
| 76 | unsigned int subno; /* index used for generating unique filenames */ | |||
| 77 | unsigned int force_display; | |||
| 78 | unsigned int nummap; /* length of map array */ | |||
| 79 | struct colormap *map; | |||
| 80 | /* array where entry 0 is for colours as specified in SPU, other entries | |||
| 81 | are for button colours as specified in PGC, so if they overlap, the | |||
| 82 | last entry takes precedence */ | |||
| 83 | struct spu *next; /* linked list */ | |||
| 84 | }; | |||
| 85 | ||||
| 86 | static struct spu | |||
| 87 | *pending_spus = 0; | |||
| 88 | ||||
| 89 | struct button /* information about a button */ | |||
| 90 | { | |||
| 91 | char *name; | |||
| 92 | bool_Bool autoaction; | |||
| 93 | int x1, y1, x2, y2; | |||
| 94 | char *up, *down, *left, *right; /* names of neighbouring buttons */ | |||
| 95 | int grp; /* which group button belongs to */ | |||
| 96 | }; | |||
| 97 | ||||
| 98 | struct dispdetails /* information about button grouping */ | |||
| 99 | { | |||
| 100 | int pts[2]; /* start time, end time */ | |||
| 101 | int numpal; /* nr used entries in palette */ | |||
| 102 | uint32_t palette[16]; /* RGB colours */ | |||
| 103 | int numcoli; /* nr of SL_COLI entries present, not checked! */ | |||
| 104 | uint32_t coli[6]; /* up to 3 8-byte SL_COLI entries */ | |||
| 105 | int numbuttons; /* length of buttons array */ | |||
| 106 | struct button *buttons; /* array */ | |||
| 107 | struct dispdetails *next; /* linked list */ | |||
| 108 | }; | |||
| 109 | ||||
| 110 | static struct dispdetails | |||
| 111 | *pending_buttons = 0; | |||
| 112 | ||||
| 113 | struct colormap /* for determining which colours take precedence in overlapping areas */ | |||
| 114 | { | |||
| 115 | uint16_t color; /* four 4-bit colour indexes */ | |||
| 116 | uint16_t contrast; /* four 4-bit contrast (transparency) values */ | |||
| 117 | int x1, y1, x2, y2; /* bounds of area over which entries are valid */ | |||
| 118 | }; | |||
| 119 | ||||
| 120 | static unsigned int read4(const unsigned char *p) | |||
| 121 | /* decodes 4 bytes as a big-endian integer starting at p. */ | |||
| 122 | { | |||
| 123 | return | |||
| 124 | p[0] << 24 | |||
| 125 | | | |||
| 126 | p[1] << 16 | |||
| 127 | | | |||
| 128 | p[2] << 8 | |||
| 129 | | | |||
| 130 | p[3]; | |||
| 131 | } /*read4*/ | |||
| 132 | ||||
| 133 | static unsigned int read2(const unsigned char *p) | |||
| 134 | /* decodes 2 bytes as a big-endian integer starting at p. */ | |||
| 135 | { | |||
| 136 | return | |||
| 137 | p[0] << 8 | |||
| 138 | | | |||
| 139 | p[1]; | |||
| 140 | } /*read2*/ | |||
| 141 | ||||
| 142 | static char *readpstr(const unsigned char *b, int *i) | |||
| 143 | /* extracts a null-terminated string beginning at b[*i], advances *i past it and returns | |||
| 144 | a copy of the string. */ | |||
| 145 | { | |||
| 146 | char * const s = strdup((const char *)b + i[0])(__extension__ (__builtin_constant_p ((const char *)b + i[0]) && ((size_t)(const void *)(((const char *)b + i[0]) + 1) - (size_t)(const void *)((const char *)b + i[0]) == 1) ? ( ((const char *) ((const char *)b + i[0]))[0] == '\0' ? (char * ) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen ( (const char *)b + i[0]) + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, (const char *)b + i[0], __len); __retval; })) : __strdup ((const char *)b + i[0]))); | |||
| 147 | i[0] += strlen(s) + 1; | |||
| 148 | return s; | |||
| 149 | } /*readpstr*/ | |||
| 150 | ||||
| 151 | static unsigned char get_next_bits() | |||
| 152 | /* returns next nibble from sub at offset ofs. */ | |||
| 153 | { | |||
| 154 | if (!have_bits) | |||
| 155 | { | |||
| 156 | next_bits = sub[ofs++]; | |||
| 157 | have_bits = true1; | |||
| 158 | return next_bits >> 4; | |||
| 159 | } /*if*/ | |||
| 160 | have_bits = false0; | |||
| 161 | return next_bits & 15; | |||
| 162 | } /*get_next_bits*/ | |||
| 163 | ||||
| 164 | static unsigned char get_next_svcdbits() | |||
| 165 | /* returns next two bits from sub at offset ofs. */ | |||
| 166 | { | |||
| 167 | switch (have_bits) | |||
| 168 | { | |||
| 169 | case 0: | |||
| 170 | ++have_bits; | |||
| 171 | return sub[++ofs] >> 6; | |||
| 172 | break; | |||
| 173 | case 1: | |||
| 174 | ++have_bits; | |||
| 175 | return (sub[ofs] & 0x30) >> 4; | |||
| 176 | break; | |||
| 177 | case 2: | |||
| 178 | ++have_bits; | |||
| 179 | return (sub[ofs] & 0x0c) >> 2; | |||
| 180 | break; | |||
| 181 | default: | |||
| 182 | have_bits = 0; | |||
| 183 | return sub[ofs] & 0x03; | |||
| 184 | break; | |||
| 185 | } /*switch*/ | |||
| 186 | } /*get_next_svcdbits*/ | |||
| 187 | ||||
| 188 | static unsigned int getpts(const unsigned char *buf) | |||
| 189 | /* decodes a presentation time stamp (PTS) beginning at location buf. */ | |||
| 190 | { | |||
| 191 | if | |||
| 192 | ( | |||
| 193 | !(buf[1] & 0xc0) | |||
| 194 | || | |||
| 195 | buf[2] < 4 | |||
| 196 | || | |||
| 197 | (buf[3] & 0xe1) != 0x21 | |||
| 198 | || | |||
| 199 | (buf[5] & 1) != 1 | |||
| 200 | || | |||
| 201 | (buf[7] & 1) != 1 | |||
| 202 | ) | |||
| 203 | return -1; /* doesn't look like a proper PTS */ | |||
| 204 | return | |||
| 205 | (buf[7] >> 1) | |||
| 206 | + | |||
| 207 | ((unsigned int)buf[6] << 7) | |||
| 208 | + | |||
| 209 | (((unsigned int)buf[5] & 254) << 14) | |||
| 210 | + | |||
| 211 | ((unsigned int)buf[4] << 22) | |||
| 212 | + | |||
| 213 | (((unsigned int)buf[3] & 14) << 29); | |||
| 214 | } /*getpts*/ | |||
| 215 | ||||
| 216 | static void addspu(struct spu *s) | |||
| 217 | /* appends s onto pending_spus. */ | |||
| 218 | { | |||
| 219 | struct spu **f = &pending_spus; | |||
| 220 | while (*f) | |||
| 221 | f = &f[0]->next; | |||
| 222 | *f = s; | |||
| 223 | } /*addspu*/ | |||
| 224 | ||||
| 225 | static void add_pending_buttons(struct dispdetails *d) | |||
| 226 | /* appends d onto pending_buttons. */ | |||
| 227 | { | |||
| 228 | struct dispdetails **dp = &pending_buttons; | |||
| 229 | while (*dp) | |||
| 230 | dp = &dp[0]->next; | |||
| 231 | *dp = d; | |||
| 232 | } /*add_pending_buttons*/ | |||
| 233 | ||||
| 234 | static int dvddecode() | |||
| 235 | /* decodes DVD-Video subpicture data from sub and appends a new entry containing | |||
| 236 | the results onto pending_spus. */ | |||
| 237 | { | |||
| 238 | unsigned int io; | |||
| 239 | uint16_t total_spu_size, dsize, thiscmdoffset, nextcmdoffset, i, x, y, t; | |||
| 240 | unsigned char c; | |||
| 241 | struct spu *newspu; | |||
| 242 | total_spu_size = read2(sub); /* SPDSZ = size of SPU */ | |||
| 243 | dsize = read2(sub + 2); /* SP_DCSQTA = offset to SP_DCSQT */ | |||
| 244 | ofs = -1; | |||
| 245 | if (debug > 1) | |||
| ||||
| 246 | fprintf(stderrstderr, "packet: %d bytes, first block offset=%d\n", total_spu_size, dsize); | |||
| 247 | newspu = malloc(sizeof(struct spu)); | |||
| 248 | memset(newspu, 0, sizeof(struct spu)); | |||
| 249 | newspu->subno = subno++; | |||
| 250 | newspu->pts[0] = newspu->pts[1] = -1; | |||
| 251 | newspu->nummap = 1; | |||
| 252 | newspu->map = malloc(sizeof(struct colormap)); | |||
| 253 | memset(newspu->map, 0, sizeof(struct colormap)); | |||
| 254 | newspu->map[0].x2 = 0x7fffffff; | |||
| 255 | newspu->map[0].y2 = 0x7fffffff; | |||
| 256 | i = dsize + 4; /* start of commands */ | |||
| 257 | thiscmdoffset = dsize; | |||
| 258 | nextcmdoffset = read2(sub + thiscmdoffset + 2); | |||
| 259 | if (nextcmdoffset < dsize) | |||
| 260 | { | |||
| 261 | if (debug > 0) | |||
| 262 | { | |||
| 263 | fprintf | |||
| 264 | ( | |||
| 265 | stderrstderr, | |||
| 266 | "invalid control header nextcommand=%d dsize=%d!\n", | |||
| 267 | nextcmdoffset, | |||
| 268 | dsize | |||
| 269 | ); | |||
| 270 | } /*if*/ | |||
| 271 | nextcmdoffset = thiscmdoffset; | |||
| 272 | } /*if*/ | |||
| 273 | t = read2(sub + dsize); /* SP_DCSQ_STM = delay in 90kHz units / 1024 before executing commands */ | |||
| 274 | if (debug > 2) | |||
| 275 | fprintf | |||
| 276 | ( | |||
| 277 | stderrstderr, | |||
| 278 | "\tBLK(%5d): time offset: %d; next: %d\n", | |||
| 279 | dsize, t, read2(sub + dsize + 2) | |||
| 280 | ); | |||
| 281 | /* decode the commands, including finding out where the image data is */ | |||
| 282 | while (i < total_spu_size) | |||
| 283 | { | |||
| 284 | c = sub[i]; /* get next command */ | |||
| 285 | switch (c) | |||
| 286 | { | |||
| 287 | case SPU_FSTA_DSP: | |||
| 288 | if (debug > 4) | |||
| 289 | fprintf(stderrstderr, "\tcmd(%5d): force start display\n", i); | |||
| 290 | newspu->force_display = true1; | |||
| 291 | // fall through | |||
| 292 | case SPU_STA_DSP: | |||
| 293 | if (debug > 4 && c == SPU_STA_DSP) | |||
| 294 | fprintf(stderrstderr, "\tcmd(%5d): start display\n", i); | |||
| 295 | i++; | |||
| 296 | newspu->pts[0] = t * 1024 + spts; | |||
| 297 | break; | |||
| 298 | ||||
| 299 | case SPU_STP_DSP: | |||
| 300 | if (debug > 4) | |||
| 301 | fprintf(stderrstderr, "\tcmd(%5d): end display\n", i); | |||
| 302 | newspu->pts[1] = t * 1024 + spts; | |||
| 303 | i++; | |||
| 304 | break; | |||
| 305 | ||||
| 306 | case SPU_SET_COLOR: | |||
| 307 | if (debug > 4) | |||
| 308 | fprintf(stderrstderr, "\tcmd(%5d): palette=%02x%02x\n", i, sub[i + 1], sub[i + 2]); | |||
| 309 | newspu->map[0].color = read2(sub + i + 1); | |||
| 310 | i += 3; | |||
| 311 | break; | |||
| 312 | ||||
| 313 | case SPU_SET_CONTR: | |||
| 314 | if (debug > 4) | |||
| 315 | fprintf(stderrstderr, "\tcmd(%5d): transparency=%02x%02x\n", i, sub[i + 1], sub[i + 2]); | |||
| 316 | newspu->map[0].contrast = read2(sub + i + 1); | |||
| 317 | i += 3; | |||
| 318 | break; | |||
| 319 | ||||
| 320 | case SPU_SET_DAREA: | |||
| 321 | newspu->x0 = ((((unsigned int)sub[i + 1]) << 4) + (sub[i + 2] >> 4)); | |||
| 322 | newspu->xd = (((sub[i + 2] & 0x0f) << 8) + sub[i + 3]) - newspu->x0 + 1; | |||
| 323 | newspu->y0 = ((((unsigned int)sub[i + 4]) << 4) + (sub[i + 5] >> 4)); | |||
| 324 | newspu->yd = (((sub[i + 5] & 0x0f) << 8) + sub[i + 6]) - newspu->y0 + 1; | |||
| 325 | if (debug > 4) | |||
| 326 | fprintf | |||
| 327 | ( | |||
| 328 | stderrstderr, | |||
| 329 | "\tcmd(%5d): image corner=%d,%d, size=%d,%d\n", | |||
| 330 | i, newspu->x0, newspu->y0, newspu->xd, newspu->yd | |||
| 331 | ); | |||
| 332 | i += 7; | |||
| 333 | break; | |||
| 334 | ||||
| 335 | case SPU_SET_DSPXA: | |||
| 336 | if (ofs >= 0) | |||
| 337 | fprintf(stderrstderr, "WARN: image pointer already supplied for this subpicture\n"); | |||
| 338 | /* not necessarily wrong, it's just I can't handle it */ | |||
| 339 | ofs = read2(sub + i + 1); /* offset to top field data */ | |||
| 340 | ofs1 = read2(sub + i + 3); /* offset to bottom field data */ | |||
| 341 | if (debug > 4) | |||
| 342 | fprintf(stderrstderr, "\tcmd(%5d): image offsets=%d,%d\n", i, ofs, ofs1); | |||
| 343 | i += 5; | |||
| 344 | break; | |||
| 345 | ||||
| 346 | case SPU_CMD_END: | |||
| 347 | if (thiscmdoffset == nextcmdoffset) /* no next SP_DCSQ */ | |||
| 348 | { | |||
| 349 | if (debug > 4) | |||
| 350 | { | |||
| 351 | fprintf(stderrstderr, "cmd: last end command\n"); | |||
| 352 | if (i + 1 < total_spu_size) | |||
| 353 | { | |||
| 354 | fprintf | |||
| 355 | ( | |||
| 356 | stderrstderr, | |||
| 357 | "data present after last command (%d bytes, size=%d)\n", | |||
| 358 | total_spu_size - (i + 1), | |||
| 359 | total_spu_size | |||
| 360 | ); | |||
| 361 | } /*if*/ | |||
| 362 | } /*if*/ | |||
| 363 | i = total_spu_size; /* indicate I'm finished */ | |||
| 364 | break; | |||
| 365 | } /*if*/ | |||
| 366 | if (debug > 4) | |||
| 367 | fprintf(stderrstderr, "\tcmd(%5d): end cmd\n", i); | |||
| 368 | /* another SP_DCSQT follows */ | |||
| 369 | thiscmdoffset = nextcmdoffset; | |||
| 370 | nextcmdoffset = read2(sub + thiscmdoffset + 2); | |||
| 371 | if (nextcmdoffset < dsize) | |||
| 372 | { | |||
| 373 | if (debug > 0) | |||
| 374 | { | |||
| 375 | fprintf | |||
| 376 | ( | |||
| 377 | stderrstderr, | |||
| 378 | "invalid control header nextcommand=%d dsize=%d!\n", | |||
| 379 | nextcmdoffset, | |||
| 380 | dsize | |||
| 381 | ); | |||
| 382 | } /*if*/ | |||
| 383 | nextcmdoffset = thiscmdoffset; | |||
| 384 | i = total_spu_size; /* force an end to all this */ | |||
| 385 | break; | |||
| 386 | } /*if*/ | |||
| 387 | t = read2(sub + thiscmdoffset); /* SP_DCSQ_STM = delay in 90kHz units / 1024 before executing commands */ | |||
| 388 | if (debug > 4) | |||
| 389 | { | |||
| 390 | fprintf(stderrstderr, "\tcmd(%5d): end cmd\n", i); | |||
| 391 | fprintf(stderrstderr, "\tBLK(%5d): time offset: %d; next: %d\n", i + 1, t, read2(sub + i + 3)); | |||
| 392 | } /*if*/ | |||
| 393 | if (debug > 4 && i + 1 < thiscmdoffset) | |||
| 394 | { | |||
| 395 | fprintf(stderrstderr, "next packet jump: %d bytes\n", thiscmdoffset - (i + 1)); | |||
| 396 | } /*if*/ | |||
| 397 | i = thiscmdoffset + 4; /* start of next lot of commands */ | |||
| 398 | break; | |||
| 399 | ||||
| 400 | /* case SPU_CHG_COLCON: */ /* fixme: not handled */ | |||
| 401 | default: | |||
| 402 | if (debug > 4) | |||
| ||||
| 403 | fprintf(stderrstderr, "\tcmd(%5d): 0x%x\n", i, c); | |||
| 404 | if (debug > 0) | |||
| 405 | fprintf(stderrstderr, "invalid sequence in control header (%02x)!\n", c); | |||
| 406 | return -1; | |||
| 407 | } /*switch*/ | |||
| 408 | } /*while*/ | |||
| 409 | ||||
| 410 | /* now to decode the actual image data */ | |||
| 411 | have_bits = false0; | |||
| 412 | x = y = 0; | |||
| 413 | io = 0; | |||
| 414 | newspu->img = malloc(newspu->xd * newspu->yd); | |||
| 415 | if (ofs < 0 && y < newspu->yd) | |||
| 416 | { | |||
| 417 | fprintf(stderrstderr, "WARN: No image data supplied for this subtitle\n"); | |||
| 418 | } | |||
| 419 | else | |||
| 420 | { | |||
| 421 | /* decode image data */ | |||
| 422 | while (ofs < dsize && y < newspu->yd) | |||
| 423 | { | |||
| 424 | i = get_next_bits(); | |||
| 425 | if (i < 4) | |||
| 426 | { | |||
| 427 | i = (i << 4) + get_next_bits(); | |||
| 428 | if (i < 16) | |||
| 429 | { | |||
| 430 | i = (i << 4) + get_next_bits(); | |||
| 431 | if (i < 0x40) | |||
| 432 | { | |||
| 433 | i = (i << 4) + get_next_bits(); | |||
| 434 | if (i < 4) | |||
| 435 | { | |||
| 436 | i = i + (newspu->xd - x) * 4; /* run ends at end of line */ | |||
| 437 | } /*if*/ | |||
| 438 | } /*if*/ | |||
| 439 | } /*if*/ | |||
| 440 | } /*if*/ | |||
| 441 | c = i & 3; /* pixel value */ | |||
| 442 | i = i >> 2; /* count */ | |||
| 443 | while (i--) | |||
| 444 | { | |||
| 445 | newspu->img[io++] = c; | |||
| 446 | if (++x == newspu->xd) | |||
| 447 | { | |||
| 448 | /* end of scanline */ | |||
| 449 | y += 2; | |||
| 450 | x = 0; | |||
| 451 | if (y >= newspu->yd && !(y & 1)) | |||
| 452 | { | |||
| 453 | /* end of top (odd) field, now do bottom (even) field */ | |||
| 454 | y = 1; | |||
| 455 | io = newspu->xd; | |||
| 456 | ofs = ofs1; | |||
| 457 | } | |||
| 458 | else | |||
| 459 | io += newspu->xd; /* next scanline */ | |||
| 460 | have_bits = false0; | |||
| 461 | } /*if*/ | |||
| 462 | } /*while*/ | |||
| 463 | } /*while*/ | |||
| 464 | } /*if*/ | |||
| 465 | if (newspu->pts[0] == -1) | |||
| 466 | return 0; /* fixme: free newspu or report error? */ | |||
| 467 | newspu->pts[0] += add_offset; | |||
| 468 | if (newspu->pts[1] != -1) | |||
| 469 | newspu->pts[1] += add_offset; | |||
| 470 | addspu(newspu); | |||
| 471 | return 0; | |||
| 472 | } /*dvddecode*/ | |||
| 473 | ||||
| 474 | /* | |||
| 475 | * from Y -> R | |||
| 476 | * from V -> G | |||
| 477 | * from U -> B | |||
| 478 | */ | |||
| 479 | ||||
| 480 | static void ycrcb_to_rgb(int *Y, int *Cr, int *Cb) | |||
| 481 | { | |||
| 482 | int R, G, B; | |||
| 483 | R = YCrCb2R(*Y,*Cr,*Cb); | |||
| 484 | G = YCrCb2G(*Y,*Cr,*Cb); | |||
| 485 | B = YCrCb2B(*Y,*Cr,*Cb); | |||
| 486 | *Y = R; | |||
| 487 | *Cr = G; | |||
| 488 | *Cb = B; | |||
| 489 | } | |||
| 490 | ||||
| 491 | static void absorb_palette(const struct dispdetails *d) | |||
| 492 | /* extracts the colour palette from d and puts it in RGB format into current_palette. */ | |||
| 493 | { | |||
| 494 | int i; | |||
| 495 | for (i = 0; i < d->numpal; i++) | |||
| 496 | { | |||
| 497 | int Y, Cr, Cb; | |||
| 498 | Y = d->palette[i] >> 16 & 255; | |||
| 499 | Cr = d->palette[i] >> 8 & 255; | |||
| 500 | Cb = d->palette[i] & 255; | |||
| 501 | current_palette[i].r = YCrCb2R(Y, Cr, Cb); | |||
| 502 | current_palette[i].g = YCrCb2G(Y, Cr, Cb); | |||
| 503 | current_palette[i].b = YCrCb2B(Y, Cr, Cb); | |||
| 504 | } /*for*/ | |||
| 505 | } /*absorb_palette*/ | |||
| 506 | ||||
| 507 | static void pluck_pending_buttons() | |||
| 508 | /* removes the head of pending_buttons, copies its palette into current_palette, | |||
| 509 | and gets rid of it. */ | |||
| 510 | { | |||
| 511 | struct dispdetails * const d = pending_buttons; | |||
| 512 | int i; | |||
| 513 | pending_buttons = d->next; | |||
| 514 | absorb_palette(d); | |||
| 515 | for (i = 0; i < d->numbuttons; i++) | |||
| 516 | { | |||
| 517 | free(d->buttons[i].name); | |||
| 518 | free(d->buttons[i].up); | |||
| 519 | free(d->buttons[i].down); | |||
| 520 | free(d->buttons[i].left); | |||
| 521 | free(d->buttons[i].right); | |||
| 522 | } /*for*/ | |||
| 523 | free(d->buttons); | |||
| 524 | free(d); | |||
| 525 | } /*pluck_pending_buttons*/ | |||
| 526 | ||||
| 527 | static unsigned char cmap_find | |||
| 528 | ( | |||
| 529 | int x, | |||
| 530 | int y, | |||
| 531 | const struct colormap *map, /* array */ | |||
| 532 | int nummap, /* length of map array */ | |||
| 533 | int ci /* pixel value */ | |||
| 534 | ) | |||
| 535 | /* returns the colour index (low nibble) and contrast (high nibble) of the | |||
| 536 | specified pixel, as determined from the highest-numbered entry of map that | |||
| 537 | covers its coordinates. Returns 0 if no entry is found. */ | |||
| 538 | { | |||
| 539 | int i; | |||
| 540 | unsigned char cix = 0; | |||
| 541 | for (i = 0; i < nummap; i++) | |||
| 542 | /* fixme: why not just start from the other end and terminate when a match is found? */ | |||
| 543 | if | |||
| 544 | ( | |||
| 545 | x >= map[i].x1 | |||
| 546 | && | |||
| 547 | y >= map[i].y1 | |||
| 548 | && | |||
| 549 | x <= map[i].x2 | |||
| 550 | && | |||
| 551 | y <= map[i].y2 | |||
| 552 | ) | |||
| 553 | cix = | |||
| 554 | (map[i].contrast >> ci * 4 & 15) << 4 | |||
| 555 | | | |||
| 556 | map[i].color >> ci * 4 & 15; | |||
| 557 | return cix; | |||
| 558 | } /*cmap_find*/ | |||
| 559 | ||||
| 560 | static int write_png | |||
| 561 | ( | |||
| 562 | const char *file_name, | |||
| 563 | const struct spu *s, | |||
| 564 | const struct colormap *map, /* array of entries for assigning colours to overlapping areas */ | |||
| 565 | int nummap /* length of map array */ | |||
| 566 | ) | |||
| 567 | /* outputs the contents of s as a PNG file, converting pixels to colours | |||
| 568 | according to map. */ | |||
| 569 | { | |||
| 570 | int status = -1; /* to begin with */ | |||
| 571 | unsigned char *out_buf = NULL((void*)0); | |||
| 572 | FILE *fp = NULL((void*)0); | |||
| 573 | png_structp png_ptr = NULL((void*)0); | |||
| 574 | png_infop info_ptr = NULL((void*)0); | |||
| 575 | const unsigned short subwidth = svcd_adjust ? 704 : 720; | |||
| 576 | const unsigned short subheight = video_format == VF_NTSC ? 480 : 576; | |||
| 577 | do /*once*/ | |||
| 578 | { | |||
| 579 | out_buf = malloc(s->xd * s->yd * 4); | |||
| 580 | if (out_buf == NULL((void*)0)) | |||
| 581 | { | |||
| 582 | fprintf(stderrstderr, "ERR: unable allocate %d-byte PNG buffer\n", s->xd * s->yd * 4); | |||
| 583 | break; | |||
| 584 | } /*if*/ | |||
| 585 | { | |||
| 586 | unsigned char *temp = out_buf; | |||
| 587 | bool_Bool nonzero = false0; | |||
| 588 | unsigned int x, y; | |||
| 589 | for (y = 0; y < s->yd; y++) | |||
| 590 | { | |||
| 591 | for (x = 0; x < s->xd; x++) | |||
| 592 | { | |||
| 593 | const unsigned char cix = | |||
| 594 | cmap_find(x + s->x0, y + s->y0, map, nummap, s->img[y * s->xd + x]); | |||
| 595 | *temp++ = current_palette[cix & 15].r; | |||
| 596 | *temp++ = current_palette[cix & 15].g; | |||
| 597 | *temp++ = current_palette[cix & 15].b; | |||
| 598 | *temp++ = (cix >> 4) * 17; | |||
| 599 | if (cix & 0xf0) | |||
| 600 | nonzero = true1; | |||
| 601 | } /*for*/ | |||
| 602 | } /*for*/ | |||
| 603 | if (!nonzero) | |||
| 604 | { | |||
| 605 | /* all transparent, don't bother writing any image */ | |||
| 606 | status = 1; | |||
| 607 | break; | |||
| 608 | } /*if*/ | |||
| 609 | } | |||
| 610 | fp = fopen(file_name, "wb"); | |||
| 611 | if (fp == NULL((void*)0)) | |||
| 612 | { | |||
| 613 | fprintf(stderrstderr, "ERR: error %s trying to open/create file: %s\n", strerror(errno(*__errno_location ())), file_name); | |||
| 614 | break; | |||
| 615 | } /*if*/ | |||
| 616 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING"1.6.8", NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
| 617 | if (png_ptr == NULL((void*)0)) | |||
| 618 | break; | |||
| 619 | info_ptr = png_create_info_struct(png_ptr); | |||
| 620 | if (info_ptr == NULL((void*)0)) | |||
| 621 | break; | |||
| 622 | if (setjmp(png_jmpbuf(png_ptr))_setjmp ((*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf )))))) | |||
| 623 | break; | |||
| 624 | png_init_io(png_ptr, fp); | |||
| 625 | png_set_filter(png_ptr, 0, PNG_FILTER_NONE0x08); | |||
| 626 | png_set_compression_level(png_ptr, Z_BEST_COMPRESSION9); | |||
| 627 | png_set_compression_mem_level(png_ptr, 8); | |||
| 628 | png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY0); | |||
| 629 | png_set_compression_window_bits(png_ptr, 15); | |||
| 630 | png_set_compression_method(png_ptr, 8); | |||
| 631 | if (full_size) | |||
| 632 | { | |||
| 633 | png_set_IHDR | |||
| 634 | ( | |||
| 635 | /*png_ptr =*/ png_ptr, | |||
| 636 | /*info_ptr =*/ info_ptr, | |||
| 637 | /*width =*/ subwidth, | |||
| 638 | /*height =*/ subheight, | |||
| 639 | /*bit_depth =*/ 8, | |||
| 640 | /*color_type =*/ PNG_COLOR_TYPE_RGB_ALPHA(2 | 4), | |||
| 641 | /*interlace_method =*/ PNG_INTERLACE_NONE0, | |||
| 642 | /*compression_method =*/ PNG_COMPRESSION_TYPE_DEFAULT0, | |||
| 643 | /*filter_method =*/ PNG_FILTER_TYPE_DEFAULT0 | |||
| 644 | ); | |||
| 645 | } | |||
| 646 | else | |||
| 647 | { | |||
| 648 | png_set_IHDR | |||
| 649 | ( | |||
| 650 | /*png_ptr =*/ png_ptr, | |||
| 651 | /*info_ptr =*/ info_ptr, | |||
| 652 | /*width =*/ s->xd, | |||
| 653 | /*height =*/ s->yd, | |||
| 654 | /*bit_depth =*/ 8, | |||
| 655 | /*color_type =*/ PNG_COLOR_TYPE_RGB_ALPHA(2 | 4), | |||
| 656 | /*interlace_method =*/ PNG_INTERLACE_NONE0, | |||
| 657 | /*compression_method =*/ PNG_COMPRESSION_TYPE_DEFAULT0, | |||
| 658 | /*filter_method =*/ PNG_FILTER_TYPE_DEFAULT0 | |||
| 659 | ); | |||
| 660 | } /*if*/ | |||
| 661 | png_write_info(png_ptr, info_ptr); | |||
| 662 | png_set_packing(png_ptr); | |||
| 663 | { | |||
| 664 | unsigned int xd = s->xd, yd = s->yd; | |||
| 665 | png_byte *row_pointers[576]; /* big enough for both PAL and NTSC */ | |||
| 666 | unsigned int a, x, y; | |||
| 667 | if (full_size) | |||
| 668 | { | |||
| 669 | unsigned char *image; | |||
| 670 | const unsigned char *temp = out_buf; | |||
| 671 | image = malloc(subwidth * subheight * 4); | |||
| 672 | memset(image, 0, subwidth * subheight * 4); // fill image full transparent | |||
| 673 | // insert image on the correct position | |||
| 674 | for (y = s->y0; y < s->y0 + s->yd; y++) | |||
| 675 | { | |||
| 676 | unsigned char *to = &image[y * subwidth * 4 + s->x0 * 4]; | |||
| 677 | if (y >= subheight) | |||
| 678 | { | |||
| 679 | fprintf(stderrstderr, "WARN: subtitle %s truncated\n", file_name); | |||
| 680 | break; | |||
| 681 | } /*if*/ | |||
| 682 | for (x = 0; x < s->xd; x++) | |||
| 683 | { | |||
| 684 | *to++ = *temp++; | |||
| 685 | *to++ = *temp++; | |||
| 686 | *to++ = *temp++; | |||
| 687 | *to++ = *temp++; | |||
| 688 | } /*for*/ | |||
| 689 | } /*for*/ | |||
| 690 | yd = subheight; | |||
| 691 | xd = subwidth; | |||
| 692 | free(out_buf); | |||
| 693 | out_buf = image; | |||
| 694 | } /*if*/ | |||
| 695 | for (a = 0; a < yd; a++) | |||
| 696 | { | |||
| 697 | row_pointers[a] = out_buf + a * (xd * 4); | |||
| 698 | } /*for*/ | |||
| 699 | png_write_image(png_ptr, row_pointers); | |||
| 700 | } | |||
| 701 | png_write_end(png_ptr, info_ptr); | |||
| 702 | /* all successfully done */ | |||
| 703 | status = 0; | |||
| 704 | } | |||
| 705 | while (false0); | |||
| 706 | if (png_ptr != NULL((void*)0)) | |||
| 707 | { | |||
| 708 | png_destroy_write_struct(&png_ptr, &info_ptr); | |||
| 709 | } /*if*/ | |||
| 710 | if (fp != NULL((void*)0)) | |||
| 711 | { | |||
| 712 | fclose(fp); | |||
| 713 | } /*if*/ | |||
| 714 | free(out_buf); | |||
| 715 | return | |||
| 716 | status; | |||
| 717 | } /*write_png*/ | |||
| 718 | ||||
| 719 | static void write_pts(const char *preamble, int pts) | |||
| 720 | /* outputs a formatted representation of a timestamp to fdo. */ | |||
| 721 | { | |||
| 722 | fprintf | |||
| 723 | ( | |||
| 724 | fdo, | |||
| 725 | " %s=\"%02d:%02d:%02d.%02d\"", | |||
| 726 | preamble, | |||
| 727 | (pts / (60 * 60 * 90000)) % 24, | |||
| 728 | (pts / (60 * 90000)) % 60, | |||
| 729 | (pts / 90000) % 60, | |||
| 730 | (pts / 900) % 100 | |||
| 731 | ); | |||
| 732 | } /*write_pts*/ | |||
| 733 | ||||
| 734 | /* | |||
| 735 | copy the content of buf to expbuf converting '&' '<' '>' '"' | |||
| 736 | expbuf must be big enough to contain the expanded buffer | |||
| 737 | */ | |||
| 738 | static void xml_buf | |||
| 739 | ( | |||
| 740 | unsigned char *expbuf, | |||
| 741 | const unsigned char *buf | |||
| 742 | ) | |||
| 743 | { | |||
| 744 | const unsigned char *p; | |||
| 745 | do | |||
| 746 | { | |||
| 747 | switch (*buf) | |||
| 748 | { | |||
| 749 | case '&': | |||
| 750 | p = (const unsigned char *)"&"; | |||
| 751 | break; | |||
| 752 | case '<': | |||
| 753 | p = (const unsigned char *)"<"; | |||
| 754 | break; | |||
| 755 | case '>': | |||
| 756 | p = (const unsigned char *)">"; | |||
| 757 | break; | |||
| 758 | case '"': | |||
| 759 | p = (const unsigned char *)"""; | |||
| 760 | break; | |||
| 761 | default: | |||
| 762 | p = NULL((void*)0); | |||
| 763 | break; | |||
| 764 | } /*switch*/ | |||
| 765 | if (p) | |||
| 766 | { | |||
| 767 | while ((*expbuf++ = *p++)) | |||
| 768 | /* copy the representation */; | |||
| 769 | --expbuf; | |||
| 770 | } | |||
| 771 | else | |||
| 772 | *expbuf++ = *buf; /* copy as is */ | |||
| 773 | } | |||
| 774 | while (*buf++); | |||
| 775 | } /*xml_buf*/ | |||
| 776 | ||||
| 777 | static void write_menu_image | |||
| 778 | ( | |||
| 779 | const struct spu *s, | |||
| 780 | const struct dispdetails *d, | |||
| 781 | const char *type, /* name of attribute to fill in with name of generated file */ | |||
| 782 | int offset /* 0 => highlighted, 1 => selected */ | |||
| 783 | ) | |||
| 784 | /* outputs the subpicture image with the buttons in the highlighted or selected state. */ | |||
| 785 | { | |||
| 786 | unsigned char nbuf[256]; | |||
| 787 | int nummap = d->numbuttons + 1, i; | |||
| 788 | struct colormap *map = malloc(sizeof(struct colormap) * nummap); | |||
| 789 | memset(map, 0, sizeof(struct colormap)); // set the first one blank | |||
| 790 | map[0].x2 = 0x7fffffff; | |||
| 791 | map[0].y2 = 0x7fffffff; | |||
| 792 | for (i = 0; i < d->numbuttons; i++) | |||
| 793 | { | |||
| 794 | const uint32_t cc = d->coli[2 * d->buttons[i].grp - 2 + offset]; | |||
| 795 | map[i + 1].x1 = d->buttons[i].x1; | |||
| 796 | map[i + 1].y1 = d->buttons[i].y1; | |||
| 797 | map[i + 1].x2 = d->buttons[i].x2; | |||
| 798 | map[i + 1].y2 = d->buttons[i].y2; | |||
| 799 | map[i + 1].color = cc >> 16; | |||
| 800 | map[i + 1].contrast = cc; | |||
| 801 | } /*for*/ | |||
| 802 | sprintf((char *)nbuf, "%s%05d%c.png", base_name, s->subno, type[0]); | |||
| 803 | if (!write_png((char *)nbuf, s, map, nummap)) | |||
| 804 | { | |||
| 805 | unsigned char ebuf[sizeof nbuf * 6]; | |||
| 806 | xml_buf(ebuf, nbuf); | |||
| 807 | fprintf(fdo," %s=\"%s\"", type, ebuf); | |||
| 808 | } /*if*/ | |||
| 809 | free(map); | |||
| 810 | } /*write_menu_image*/ | |||
| 811 | ||||
| 812 | static void write_spu | |||
| 813 | ( | |||
| 814 | const struct spu * curspu, | |||
| 815 | const struct dispdetails * buttons /* applicable button highlight info, if any */ | |||
| 816 | ) | |||
| 817 | /* writes out all information about a subpicture unit as an <spu> tag. */ | |||
| 818 | { | |||
| 819 | unsigned char nbuf[256]; | |||
| 820 | int i; | |||
| 821 | if (buttons) | |||
| 822 | absorb_palette(buttons); | |||
| 823 | fprintf(fdo, "\t\t<spu"); | |||
| 824 | sprintf((char *)nbuf, "%s%05d.png", base_name, curspu->subno); | |||
| 825 | if (!write_png((char *)nbuf, curspu, curspu->map, curspu->nummap)) | |||
| 826 | { | |||
| 827 | unsigned char ebuf[sizeof nbuf * 6]; | |||
| 828 | xml_buf(ebuf, nbuf); | |||
| 829 | fprintf(fdo, " image=\"%s\"", ebuf); | |||
| 830 | } /*if*/ | |||
| 831 | if (buttons && buttons->numbuttons) | |||
| 832 | { | |||
| 833 | write_menu_image(curspu, buttons, "highlight", 0); | |||
| 834 | write_menu_image(curspu, buttons, "select", 1); | |||
| 835 | } /*if*/ | |||
| 836 | write_pts("start", curspu->pts[0]); | |||
| 837 | if (curspu->pts[1] != -1) | |||
| 838 | write_pts("end", curspu->pts[1]); | |||
| 839 | if (curspu->x0 || curspu->y0) | |||
| 840 | fprintf(fdo, " xoffset=\"%d\" yoffset=\"%d\"", curspu->x0, curspu->y0); | |||
| 841 | if (curspu->force_display) | |||
| 842 | fprintf(fdo, " force=\"yes\""); | |||
| 843 | if (buttons && buttons->numbuttons) | |||
| 844 | { | |||
| 845 | fprintf(fdo, ">\n"); | |||
| 846 | for (i = 0; i < buttons->numbuttons; i++) | |||
| 847 | { | |||
| 848 | const struct button * const b = buttons->buttons + i; | |||
| 849 | fprintf | |||
| 850 | ( | |||
| 851 | fdo, | |||
| 852 | "\t\t\t<%s name=\"%s\" x0=\"%d\" y0=\"%d\" x1=\"%d\" y1=\"%d\"" | |||
| 853 | " up=\"%s\" down=\"%s\" left=\"%s\" right=\"%s\" />\n", | |||
| 854 | b->autoaction ? "action" : "button", b->name, | |||
| 855 | b->x1, b->y1, b->x2, b->y2, | |||
| 856 | b->up, b->down, b->left, b->right | |||
| 857 | ); | |||
| 858 | } /*for*/ | |||
| 859 | fprintf(fdo, "\t\t</spu>\n"); | |||
| 860 | } | |||
| 861 | else | |||
| 862 | fprintf(fdo, " />\n"); | |||
| 863 | } /*write_spu*/ | |||
| 864 | ||||
| 865 | static void flushspus(unsigned int lasttime) | |||
| 866 | /* pops and outputs elements from pending_spus and pending_buttons that start | |||
| 867 | prior to lasttime. */ | |||
| 868 | { | |||
| 869 | while (pending_spus) | |||
| 870 | { | |||
| 871 | const struct spu * const curspu = pending_spus; | |||
| 872 | if (curspu->pts[0] >= lasttime) | |||
| 873 | return; /* next entry not yet due */ | |||
| 874 | pending_spus = pending_spus->next; | |||
| 875 | while | |||
| 876 | ( | |||
| 877 | pending_buttons | |||
| 878 | && | |||
| 879 | pending_buttons->pts[1] < curspu->pts[0] | |||
| 880 | && | |||
| 881 | pending_buttons->pts[1] != -1 | |||
| 882 | ) | |||
| 883 | /* merge colours from expired entries into colour table, but otherwise ignore them */ | |||
| 884 | pluck_pending_buttons(); | |||
| 885 | if | |||
| 886 | ( | |||
| 887 | pending_buttons | |||
| 888 | && | |||
| 889 | (pending_buttons->pts[0] < curspu->pts[1] || curspu->pts[1] == -1) | |||
| 890 | && | |||
| 891 | (pending_buttons->pts[1] > curspu->pts[0] || pending_buttons->pts[1] == -1) | |||
| 892 | ) | |||
| 893 | /* head of pending_buttons overlaps duration of curspu */ | |||
| 894 | write_spu(curspu, pending_buttons); | |||
| 895 | else | |||
| 896 | write_spu(curspu, 0); | |||
| 897 | free(curspu->img); | |||
| 898 | free(curspu->map); | |||
| 899 | free((void *)curspu); | |||
| 900 | } /*while*/ | |||
| 901 | } /*flushspus*/ | |||
| 902 | ||||
| 903 | #define bps(n,R,G,B)do { current_palette[n].r = R; current_palette[n].g = G; current_palette [n].b = B; } while (0) do { current_palette[n].r = R; current_palette[n].g = G; current_palette[n].b = B; } while (false0) | |||
| 904 | ||||
| 905 | static int svcddecode() | |||
| 906 | { | |||
| 907 | unsigned int io; | |||
| 908 | unsigned short int size, i, x, y; | |||
| 909 | unsigned char c; | |||
| 910 | struct spu *s; | |||
| 911 | int n; | |||
| 912 | size = read2(sub); | |||
| 913 | if (debug > 1) | |||
| 914 | fprintf(stderrstderr, "packet: 0x%x bytes\n", size); | |||
| 915 | s = malloc(sizeof(struct spu)); | |||
| 916 | memset(s, 0, sizeof(struct spu)); | |||
| 917 | s->subno = subno++; | |||
| 918 | s->pts[0] = spts; | |||
| 919 | s->pts[1] = -1; | |||
| 920 | s->nummap = 1; | |||
| 921 | s->map = malloc(sizeof(struct colormap)); | |||
| 922 | memset(s->map, 0, sizeof(struct colormap)); | |||
| 923 | s->map[0].x2 = 0x7ffffff; /* single colour map covers entire picture */ | |||
| 924 | s->map[0].y2 = 0x7ffffff; | |||
| 925 | i = 2; | |||
| 926 | if (sub[i] & 0x08) /* timestamp present */ | |||
| 927 | { | |||
| 928 | s->pts[1] = spts + read4(sub + i + 2); | |||
| 929 | i += 4; | |||
| 930 | } /*if*/ | |||
| 931 | i += 2; | |||
| 932 | s->x0 = read2(sub + i); | |||
| 933 | s->y0 = read2(sub + i + 2); | |||
| 934 | s->xd = read2(sub + i + 4); | |||
| 935 | s->yd = read2(sub + i + 6); | |||
| 936 | i += 8; | |||
| 937 | if (debug > 4) | |||
| 938 | fprintf(stderrstderr, "img ofs: %d,%d size: %d,%d\n", s->x0, s->y0, s->xd, s->yd); | |||
| 939 | for (n = 0; n < 4; n++) | |||
| 940 | { | |||
| 941 | /* collect colour table */ | |||
| 942 | int r, g, b; | |||
| 943 | r = sub[i + 0 + n * 4]; | |||
| 944 | g = sub[i + 1 + n * 4]; | |||
| 945 | b = sub[i + 2 + n * 4]; | |||
| 946 | ycrcb_to_rgb(&r, &g, &b); | |||
| 947 | bps(n, r, g, b)do { current_palette[n].r = r; current_palette[n].g = g; current_palette [n].b = b; } while (0); | |||
| 948 | if (debug > 4) | |||
| 949 | { | |||
| 950 | fprintf | |||
| 951 | ( | |||
| 952 | stderrstderr, | |||
| 953 | "palette: %d => 0x%02x 0x%02x 0x%02x 0x%02x => (%d, %d, %d)\n", | |||
| 954 | n, | |||
| 955 | sub[i + 0 + n * 4], | |||
| 956 | sub[i + 1 + n * 4], | |||
| 957 | sub[i + 2 + n * 4], | |||
| 958 | sub[i + 3 + n * 4], | |||
| 959 | r, | |||
| 960 | g, | |||
| 961 | b | |||
| 962 | ); | |||
| 963 | } /*if*/ | |||
| 964 | } /*for*/ | |||
| 965 | s->map[0].color = 0x3210; | |||
| 966 | s->map[0].contrast = | |||
| 967 | (sub[i + 3] >> 4) | |||
| 968 | + | |||
| 969 | (sub[i + 7] & 0xf0) | |||
| 970 | + | |||
| 971 | ((sub[i + 11] & 0xf0) << 4) | |||
| 972 | + | |||
| 973 | ((sub[i + 15] & 0xf0) << 8); | |||
| 974 | if (debug > 4) | |||
| 975 | fprintf(stderrstderr, "tpalette: %04x\n", s->map[0].contrast); | |||
| 976 | i += 16; | |||
| 977 | if (sub[i++] >> 6) | |||
| 978 | { | |||
| 979 | if (debug > 4) | |||
| 980 | { | |||
| 981 | fprintf | |||
| 982 | ( | |||
| 983 | stderrstderr, | |||
| 984 | "cmd: shift (unsupported), direction=%d time=%f\n", | |||
| 985 | sub[i - 1] >> 4 & 0x3, | |||
| 986 | read4(sub) / 90000.0 | |||
| 987 | ); | |||
| 988 | } /*if*/ | |||
| 989 | i += 4; | |||
| 990 | } /*if*/ | |||
| 991 | ofs = i + 2 - 1; // get_next_svcdbits will increment ofs by 1 | |||
| 992 | ofs1 = ofs + read2(sub + i); | |||
| 993 | /* i += 2; */ /* not further used */ | |||
| 994 | if (debug > 4) | |||
| 995 | fprintf(stderrstderr, "cmd: image offsets 0x%x 0x%x\n", ofs, ofs1); | |||
| 996 | have_bits = 0; | |||
| 997 | x = y = 0; | |||
| 998 | io = 0; | |||
| 999 | s->img = malloc(s->xd * s->yd); | |||
| 1000 | memset(s->img, 0, s->xd * s->yd); | |||
| 1001 | /* decode the pixels */ | |||
| 1002 | while (ofs < size && y < s->yd) | |||
| 1003 | { | |||
| 1004 | if ((c = get_next_svcdbits()) != 0) | |||
| 1005 | { | |||
| 1006 | s->img[io++] = c; | |||
| 1007 | ++x; | |||
| 1008 | } | |||
| 1009 | else | |||
| 1010 | { | |||
| 1011 | c = get_next_svcdbits() + 1; | |||
| 1012 | x += c; | |||
| 1013 | io += c; | |||
| 1014 | } /*if*/ | |||
| 1015 | if (x >= s->xd) | |||
| 1016 | { | |||
| 1017 | y += 2; | |||
| 1018 | x = 0; | |||
| 1019 | if (y >= s->yd && !(y & 1)) | |||
| 1020 | { | |||
| 1021 | y = 1; | |||
| 1022 | ofs = ofs1; | |||
| 1023 | } /*if*/ | |||
| 1024 | io = s->xd * y; | |||
| 1025 | have_bits = 0; | |||
| 1026 | } /*if*/ | |||
| 1027 | } /*while*/ | |||
| 1028 | s->pts[0] += add_offset; | |||
| 1029 | if (s->pts[1] != -1) | |||
| 1030 | s->pts[1] += add_offset; | |||
| 1031 | addspu(s); | |||
| 1032 | if (debug > 2) | |||
| 1033 | fprintf(stderrstderr, "ofs: 0x%x y: %d\n", ofs, y); | |||
| 1034 | return 0; | |||
| 1035 | } /*svcddecode*/ | |||
| 1036 | ||||
| 1037 | static void usage(void) | |||
| 1038 | { | |||
| 1039 | fprintf(stderrstderr, | |||
| 1040 | "\nUse: %s [options] [input file] [input file] ...\n\n", | |||
| 1041 | "spuunmux"); | |||
| 1042 | fprintf(stderrstderr, "options:\n"); | |||
| 1043 | fprintf(stderrstderr, | |||
| 1044 | "-o <name> base name for script and images [sub]\n"); | |||
| 1045 | fprintf(stderrstderr, | |||
| 1046 | "-v <level> verbosity level [0]\n"); | |||
| 1047 | fprintf(stderrstderr, | |||
| 1048 | "-f resize images to full size [720x576 or 720x480]\n"); | |||
| 1049 | fprintf(stderrstderr, | |||
| 1050 | "-F <format> specify video format, NTSC or PAL\n"); | |||
| 1051 | fprintf(stderrstderr, | |||
| 1052 | "-s <stream> number of the substream to extract [0]\n"); | |||
| 1053 | fprintf(stderrstderr, | |||
| 1054 | "-p <file> name of file with dvd palette [none]\n"); | |||
| 1055 | fprintf(stderrstderr, " if palette file ends with .rgb\n"); | |||
| 1056 | fprintf(stderrstderr, " treated as a RGB\n"); | |||
| 1057 | fprintf(stderrstderr, " else as a YCbCr color\n"); | |||
| 1058 | fprintf(stderrstderr, "-h print this help\n"); | |||
| 1059 | fprintf(stderrstderr, "-V print version number\n"); | |||
| 1060 | fprintf(stderrstderr, "\n"); | |||
| 1061 | } /*usage*/ | |||
| 1062 | ||||
| 1063 | int main(int argc, char **argv) | |||
| 1064 | { | |||
| 1065 | int option, n; | |||
| 1066 | int firstvideo = -1; | |||
| 1067 | unsigned int pid, next_word, stream_number, fileindex, nrinfiles; | |||
| 1068 | unsigned char cbuf[CBUFSIZE65536]; | |||
| 1069 | unsigned char psbuf[PSBUFSIZE10]; | |||
| 1070 | char *palet_file; | |||
| 1071 | char *iname[256]; /* names of input files -- fixme: no range checking */ | |||
| 1072 | unsigned int last_system_time = -1; | |||
| 1073 | ||||
| 1074 | video_format = get_video_format(); | |||
| 1075 | fputs(PACKAGE_HEADER("spuunmux")"DVDAuthor" "::" "spuunmux" ", version " "0.7.1+" ".\nBuild options:" " gnugetopt" " graphicsmagick" " iconv" " freetype" " fribidi" " fontconfig" "\nSend bug reports to <" "dvdauthor-users@lists.sourceforge.net" ">\n\n", stderrstderr); | |||
| 1076 | if (video_format != VF_NONE) | |||
| 1077 | { | |||
| 1078 | fprintf | |||
| 1079 | ( | |||
| 1080 | stderrstderr, | |||
| 1081 | "INFO: default video format is %s\n", | |||
| 1082 | video_format == VF_PAL ? "PAL" : "NTSC" | |||
| 1083 | ); | |||
| 1084 | } | |||
| 1085 | else | |||
| 1086 | { | |||
| 1087 | #if defined(DEFAULT_VIDEO_FORMAT1) | |||
| 1088 | # if DEFAULT_VIDEO_FORMAT1 == 1 | |||
| 1089 | fprintf(stderrstderr, "INFO: default video format is NTSC\n"); | |||
| 1090 | video_format = VF_NTSC; | |||
| 1091 | # elif DEFAULT_VIDEO_FORMAT1 == 2 | |||
| 1092 | fprintf(stderrstderr, "INFO: default video format is PAL\n"); | |||
| 1093 | video_format = VF_PAL; | |||
| 1094 | # endif | |||
| 1095 | #else | |||
| 1096 | fprintf(stderrstderr, "INFO: no default video format, must explicitly specify NTSC or PAL\n"); | |||
| 1097 | #endif | |||
| 1098 | } /*if*/ | |||
| 1099 | base_name = "sub"; | |||
| 1100 | stream_number = 0; | |||
| 1101 | palet_file = 0; | |||
| 1102 | while ((option = getopt(argc, argv, "o:v:fF:s:p:Vh")) != -1) | |||
| 1103 | { | |||
| 1104 | switch (option) | |||
| 1105 | { | |||
| 1106 | case 'o': | |||
| 1107 | base_name = optarg; | |||
| 1108 | break; | |||
| 1109 | case 'v': | |||
| 1110 | debug = strtounsigned(optarg, "verbosity"); | |||
| 1111 | break; | |||
| 1112 | case 'f': | |||
| 1113 | full_size = true1; | |||
| 1114 | break; | |||
| 1115 | case 'F': | |||
| 1116 | if (!strcasecmp(optarg, "ntsc")) | |||
| 1117 | { | |||
| 1118 | video_format = VF_NTSC; | |||
| 1119 | } | |||
| 1120 | else if (!strcasecmp(optarg, "pal")) | |||
| 1121 | { | |||
| 1122 | video_format = VF_PAL; | |||
| 1123 | } | |||
| 1124 | else | |||
| 1125 | { | |||
| 1126 | fprintf(stderrstderr, "ERR: Unrecognized video format \"%s\"\n", optarg); | |||
| 1127 | exit(-1); | |||
| 1128 | } /*if*/ | |||
| 1129 | break; | |||
| 1130 | case 's': | |||
| 1131 | stream_number = strtounsigned(optarg, "stream number"); | |||
| 1132 | break; | |||
| 1133 | case 'p': | |||
| 1134 | palet_file = optarg; | |||
| 1135 | break; | |||
| 1136 | case 'V': | |||
| 1137 | exit(-1); | |||
| 1138 | ||||
| 1139 | case 'h': | |||
| 1140 | default: | |||
| 1141 | usage(); | |||
| 1142 | return -1; | |||
| 1143 | } /*switch*/ | |||
| 1144 | } /*while*/ | |||
| 1145 | ||||
| 1146 | if (optind < argc) | |||
| 1147 | { | |||
| 1148 | /* remaining args are input filenames */ | |||
| 1149 | int n, i; | |||
| 1150 | for (i = 0, n = optind; n < argc; n++, i++) | |||
| 1151 | iname[i] = argv[n]; | |||
| 1152 | nrinfiles = i; | |||
| 1153 | } | |||
| 1154 | else | |||
| 1155 | { | |||
| 1156 | usage(); | |||
| 1157 | return -1; | |||
| 1158 | } /*if*/ | |||
| 1159 | if (full_size && video_format == VF_NONE) | |||
| 1160 | { | |||
| 1161 | fprintf(stderrstderr, "ERR: cannot determine meaning of full size without knowing if it's NTSC or PAL\n"); | |||
| 1162 | exit(-1); | |||
| 1163 | } /*if*/ | |||
| 1164 | ||||
| 1165 | /* initialize current_palette to default palette */ | |||
| 1166 | bps(0, 0, 0, 0)do { current_palette[0].r = 0; current_palette[0].g = 0; current_palette [0].b = 0; } while (0); | |||
| 1167 | bps(1, 127, 0, 0)do { current_palette[1].r = 127; current_palette[1].g = 0; current_palette [1].b = 0; } while (0); | |||
| 1168 | bps(2, 0, 127, 0)do { current_palette[2].r = 0; current_palette[2].g = 127; current_palette [2].b = 0; } while (0); | |||
| 1169 | bps(3, 127, 127, 0)do { current_palette[3].r = 127; current_palette[3].g = 127; current_palette [3].b = 0; } while (0); | |||
| 1170 | bps(4, 0, 0, 127)do { current_palette[4].r = 0; current_palette[4].g = 0; current_palette [4].b = 127; } while (0); | |||
| 1171 | bps(5, 127, 0, 127)do { current_palette[5].r = 127; current_palette[5].g = 0; current_palette [5].b = 127; } while (0); | |||
| 1172 | bps(6, 0, 127, 127)do { current_palette[6].r = 0; current_palette[6].g = 127; current_palette [6].b = 127; } while (0); | |||
| 1173 | bps(7, 127, 127, 127)do { current_palette[7].r = 127; current_palette[7].g = 127; current_palette [7].b = 127; } while (0); | |||
| 1174 | bps(8, 192, 192, 192)do { current_palette[8].r = 192; current_palette[8].g = 192; current_palette [8].b = 192; } while (0); | |||
| 1175 | bps(9, 128, 0, 0)do { current_palette[9].r = 128; current_palette[9].g = 0; current_palette [9].b = 0; } while (0); | |||
| 1176 | bps(10, 0, 128, 0)do { current_palette[10].r = 0; current_palette[10].g = 128; current_palette [10].b = 0; } while (0); | |||
| 1177 | bps(11, 128, 128, 0)do { current_palette[11].r = 128; current_palette[11].g = 128 ; current_palette[11].b = 0; } while (0); | |||
| 1178 | bps(12, 0, 0, 128)do { current_palette[12].r = 0; current_palette[12].g = 0; current_palette [12].b = 128; } while (0); | |||
| 1179 | bps(13, 128, 0, 128)do { current_palette[13].r = 128; current_palette[13].g = 0; current_palette [13].b = 128; } while (0); | |||
| 1180 | bps(14, 0, 128, 128)do { current_palette[14].r = 0; current_palette[14].g = 128; current_palette [14].b = 128; } while (0); | |||
| 1181 | bps(15, 128, 128, 128)do { current_palette[15].r = 128; current_palette[15].g = 128 ; current_palette[15].b = 128; } while (0); | |||
| 1182 | ||||
| 1183 | if (palet_file) | |||
| 1184 | { | |||
| 1185 | bool_Bool rgb = false0; | |||
| 1186 | char * const temp = strrchr(palet_file, '.'); | |||
| 1187 | if (temp != NULL((void*)0)) | |||
| 1188 | { | |||
| 1189 | if (strcmp(temp, ".rgb")__extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (temp) && __builtin_constant_p (".rgb") && ( __s1_len = strlen (temp), __s2_len = strlen (".rgb"), (!((size_t )(const void *)((temp) + 1) - (size_t)(const void *)(temp) == 1) || __s1_len >= 4) && (!((size_t)(const void *) ((".rgb") + 1) - (size_t)(const void *)(".rgb") == 1) || __s2_len >= 4)) ? __builtin_strcmp (temp, ".rgb") : (__builtin_constant_p (temp) && ((size_t)(const void *)((temp) + 1) - (size_t )(const void *)(temp) == 1) && (__s1_len = strlen (temp ), __s1_len < 4) ? (__builtin_constant_p (".rgb") && ((size_t)(const void *)((".rgb") + 1) - (size_t)(const void * )(".rgb") == 1) ? __builtin_strcmp (temp, ".rgb") : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (".rgb"); int __result = (((const unsigned char *) ( const char *) (temp))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (temp))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (temp))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (temp))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p (".rgb") && ((size_t)(const void *)((".rgb") + 1) - ( size_t)(const void *)(".rgb") == 1) && (__s2_len = strlen (".rgb"), __s2_len < 4) ? (__builtin_constant_p (temp) && ((size_t)(const void *)((temp) + 1) - (size_t)(const void *) (temp) == 1) ? __builtin_strcmp (temp, ".rgb") : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (temp); int __result = (((const unsigned char *) (const char *) (".rgb"))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (".rgb"))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (".rgb"))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (".rgb"))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (temp, ".rgb")))); }) == 0) | |||
| 1190 | rgb = true1; | |||
| 1191 | } /*if*/ | |||
| 1192 | fdo = fopen(palet_file, "r"); | |||
| 1193 | if (fdo != NULL((void*)0)) | |||
| 1194 | { | |||
| 1195 | for (n = 0; n < 16; n++) | |||
| 1196 | { | |||
| 1197 | int r, g, b; | |||
| 1198 | fscanf(fdo, "%02x%02x%02x", &r, &g, &b); | |||
| 1199 | if (!rgb) | |||
| 1200 | ycrcb_to_rgb(&r, &g, &b); | |||
| 1201 | current_palette[n].r = r; | |||
| 1202 | current_palette[n].g = g; | |||
| 1203 | current_palette[n].b = b; | |||
| 1204 | if (debug > 3) | |||
| 1205 | fprintf | |||
| 1206 | ( | |||
| 1207 | stderrstderr, | |||
| 1208 | "pal: %d #%02x%02x%02x\n", | |||
| 1209 | n, current_palette[n].r, current_palette[n].g, current_palette[n].b | |||
| 1210 | ); | |||
| 1211 | } /*for*/ | |||
| 1212 | fclose(fdo); | |||
| 1213 | } | |||
| 1214 | else | |||
| 1215 | { | |||
| 1216 | fprintf(stderrstderr, "unable to open %s, using defaults\n", palet_file); | |||
| 1217 | } /*if*/ | |||
| 1218 | } /*if*/ | |||
| 1219 | if (strlen(base_name) > 246) | |||
| 1220 | { | |||
| 1221 | fprintf(stderrstderr, | |||
| 1222 | "error: max length of base for filename creation is 246 characters\n"); | |||
| 1223 | return -1; | |||
| 1224 | } /*if*/ | |||
| 1225 | { | |||
| 1226 | char nbuf[256]; | |||
| 1227 | sprintf(nbuf, "%s.xml", base_name); | |||
| 1228 | fdo = fopen(nbuf, "w+"); | |||
| 1229 | } | |||
| 1230 | fprintf(fdo, "<subpictures>\n\t<stream>\n"); | |||
| 1231 | pts = 0; | |||
| 1232 | subno = 0; | |||
| 1233 | subi = 0; | |||
| 1234 | add_offset = 450; // for rounding purposes | |||
| 1235 | fileindex = 0; | |||
| 1236 | while (fileindex < nrinfiles) | |||
| 1237 | { | |||
| 1238 | struct vfile fd = varied_open(iname[fileindex], O_RDONLY00, "input file"); | |||
| 1239 | if (debug > 0) | |||
| 1240 | fprintf(stderrstderr, "file: %s\n", iname[fileindex]); | |||
| 1241 | fileindex++; | |||
| 1242 | while (fread(&pid, 1, 4, fd.h) == 4) | |||
| 1243 | { | |||
| 1244 | pid = ntohl(pid)(__extension__ ({ unsigned int __v, __x = (pid); if (__builtin_constant_p (__x)) __v = ((((__x) & 0xff000000) >> 24) | (((__x ) & 0x00ff0000) >> 8) | (((__x) & 0x0000ff00) << 8) | (((__x) & 0x000000ff) << 24)); else __asm__ ( "bswap %0" : "=r" (__v) : "0" (__x)); __v; })); | |||
| 1245 | if (pid == 0x00000100 + MPID_PACK) | |||
| 1246 | { // start PS (Program stream) | |||
| 1247 | unsigned int new_system_time, stuffcount; | |||
| 1248 | l_01ba: | |||
| 1249 | if (debug > 5) | |||
| 1250 | fprintf(stderrstderr, "pack_start_code\n"); | |||
| 1251 | if (fread(psbuf, 1, PSBUFSIZE10, fd.h) < 1) | |||
| 1252 | break; | |||
| 1253 | if ((psbuf[0] & 0xc0) != 0x40) | |||
| 1254 | { | |||
| 1255 | if (debug > 1) | |||
| 1256 | fprintf(stderrstderr, "not a MPEG-2 file, skipping.\n"); | |||
| 1257 | break; | |||
| 1258 | } /*if*/ | |||
| 1259 | new_system_time = | |||
| 1260 | (psbuf[4] >> 3) | |||
| 1261 | + | |||
| 1262 | psbuf[3] * 32 | |||
| 1263 | + | |||
| 1264 | (psbuf[2] & 3) * 32 * 256 | |||
| 1265 | + | |||
| 1266 | (psbuf[2] & 0xf8) * 32 * 128 | |||
| 1267 | + | |||
| 1268 | psbuf[1] * 1024 * 1024 | |||
| 1269 | + | |||
| 1270 | (psbuf[0] & 3) * 1024 * 1024 * 256 | |||
| 1271 | + | |||
| 1272 | (psbuf[0] & 0x38) * 1024 * 1024 * 128; | |||
| 1273 | if (new_system_time < last_system_time) | |||
| 1274 | { | |||
| 1275 | if (last_system_time != -1) | |||
| 1276 | { | |||
| 1277 | if (debug > 0) | |||
| 1278 | fprintf | |||
| 1279 | ( | |||
| 1280 | stderrstderr, | |||
| 1281 | "Time changed in stream header, use old time as offset for" | |||
| 1282 | " timecode in subtitle stream\n" | |||
| 1283 | ); | |||
| 1284 | add_offset += last_system_time; | |||
| 1285 | } /*if*/ | |||
| 1286 | } /*if*/ | |||
| 1287 | last_system_time = new_system_time; | |||
| 1288 | flushspus(last_system_time); | |||
| 1289 | if (debug > 5) | |||
| 1290 | { | |||
| 1291 | fprintf(stderrstderr, "system time: %u\n", new_system_time); | |||
| 1292 | } /*if*/ | |||
| 1293 | stuffcount = psbuf[9] & 7; | |||
| 1294 | if (stuffcount != 0) | |||
| 1295 | { | |||
| 1296 | char stuff[7]; | |||
| 1297 | if (debug > 5) | |||
| 1298 | fprintf(stderrstderr, "found %d stuffing bytes\n", stuffcount); | |||
| 1299 | if (fread(stuff, 1, stuffcount, fd.h) < stuffcount) | |||
| 1300 | break; | |||
| 1301 | } /*if*/ | |||
| 1302 | } | |||
| 1303 | else if (pid == 0x100 + MPID_PROGRAM_END) | |||
| 1304 | { | |||
| 1305 | if (debug > 5) | |||
| 1306 | fprintf(stderrstderr, "end packet\n"); | |||
| 1307 | } | |||
| 1308 | else /* packet with a length field */ | |||
| 1309 | { | |||
| 1310 | unsigned short int package_length; | |||
| 1311 | fread(&package_length, 1, 2, fd.h); | |||
| 1312 | package_length = ntohs(package_length)(__extension__ ({ unsigned short int __v, __x = (unsigned short int) (package_length); if (__builtin_constant_p (__x)) __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | ((( __x) & 0xff) << 8))); else __asm__ ("rorw $8, %w0" : "=r" (__v) : "0" (__x) : "cc"); __v; })); | |||
| 1313 | if (package_length != 0) | |||
| 1314 | { | |||
| 1315 | switch (pid) | |||
| 1316 | { | |||
| 1317 | case 0x0100 + MPID_SYSTEM: | |||
| 1318 | if (debug > 5) | |||
| 1319 | fprintf(stderrstderr, "system header\n"); | |||
| 1320 | break; | |||
| 1321 | case 0x0100 + MPID_PRIVATE2: /* PCI & DSI packets, not my problem */ | |||
| 1322 | if (debug > 5) | |||
| 1323 | fprintf(stderrstderr, "private stream 2\n"); | |||
| 1324 | break; | |||
| 1325 | case 0x0100 + MPID_PRIVATE1: /* subpicture or audio stream */ | |||
| 1326 | if (debug > 5) | |||
| 1327 | fprintf(stderrstderr, "private stream 1\n"); | |||
| 1328 | fread(cbuf, 1, package_length, fd.h); | |||
| 1329 | next_word = getpts(cbuf); | |||
| 1330 | if (next_word != -1) | |||
| 1331 | { | |||
| 1332 | pts = next_word; | |||
| 1333 | } /*if*/ | |||
| 1334 | next_word = cbuf[2] /* additional data length */ + 3 /* length of fixed part of MPEG-2 extension */; | |||
| 1335 | if (debug > 5) | |||
| 1336 | { | |||
| 1337 | /* dump PES header + extension */ | |||
| 1338 | int c; | |||
| 1339 | for (c = 0; c < next_word; c++) | |||
| 1340 | fprintf(stderrstderr, "0x%02x ", cbuf[c]); | |||
| 1341 | } /*if*/ | |||
| 1342 | if (debug > 5) | |||
| 1343 | fprintf(stderrstderr, "tid: %d\n", pts); | |||
| 1344 | if | |||
| 1345 | ( | |||
| 1346 | cbuf[next_word] == stream_number + 32 /* DVD-Video stream nr */ | |||
| 1347 | || | |||
| 1348 | cbuf[next_word] == 0x70 && cbuf[next_word + 1] == stream_number | |||
| 1349 | /* SVCD stream nr */ | |||
| 1350 | ) | |||
| 1351 | { | |||
| 1352 | /* this is the subpicture stream the user wants dumped */ | |||
| 1353 | svcd_adjust = cbuf[next_word] == 0x70 ? 4 : 0; | |||
| 1354 | if (/*debug < 6 &&*/ debug > 1) | |||
| 1355 | { | |||
| 1356 | fprintf(stderrstderr, | |||
| 1357 | "id: 0x%x 0x%x %d tid: %d\n", | |||
| 1358 | cbuf[next_word], package_length, | |||
| 1359 | next_word, pts); | |||
| 1360 | } /*if*/ | |||
| 1361 | if (!subi) | |||
| 1362 | { | |||
| 1363 | /* starting a new SPU */ | |||
| 1364 | subs = | |||
| 1365 | ((unsigned int)cbuf[next_word + 1 + svcd_adjust] << 8) | |||
| 1366 | + | |||
| 1367 | cbuf[next_word + 2 + svcd_adjust]; | |||
| 1368 | /* SPDSZ, size of total subpicture data */ | |||
| 1369 | spts = pts; | |||
| 1370 | } /*if*/ | |||
| 1371 | memcpy | |||
| 1372 | ( | |||
| 1373 | /*dest =*/ sub + subi, | |||
| 1374 | /*src =*/ cbuf + next_word + 1 + svcd_adjust, | |||
| 1375 | /*n =*/ package_length - next_word - 1 - svcd_adjust | |||
| 1376 | ); | |||
| 1377 | /* collect the subpicture data */ | |||
| 1378 | if (debug > 1) | |||
| 1379 | { | |||
| 1380 | fprintf(stderrstderr, "found %d bytes of data\n", | |||
| 1381 | package_length - next_word - 1 - svcd_adjust); | |||
| 1382 | } /*if*/ | |||
| 1383 | subi += package_length - next_word - 1 - svcd_adjust; | |||
| 1384 | /* how much I just collected */ | |||
| 1385 | if (debug > 2) | |||
| 1386 | { | |||
| 1387 | fprintf(stderrstderr, | |||
| 1388 | "subi: %d (0x%x) subs: %d (0x%x) b-a-1: %d (0x%x)\n", | |||
| 1389 | subi, subi, subs, subs, | |||
| 1390 | package_length - next_word - 1 - svcd_adjust, | |||
| 1391 | package_length - next_word - 1 - svcd_adjust); | |||
| 1392 | } /*if*/ | |||
| 1393 | if (svcd_adjust) | |||
| 1394 | { | |||
| 1395 | if (cbuf[next_word + 2] & 0x80) | |||
| 1396 | { | |||
| 1397 | subi = 0; | |||
| 1398 | next_word = svcddecode(); | |||
| 1399 | if (next_word) | |||
| 1400 | { | |||
| 1401 | fprintf | |||
| 1402 | ( | |||
| 1403 | stderrstderr, | |||
| 1404 | "found unreadable subtitle at %.2fs, skipping\n", | |||
| 1405 | (double) spts / 90000 | |||
| 1406 | ); | |||
| 1407 | continue; | |||
| 1408 | } /*if*/ | |||
| 1409 | } /*if*/ | |||
| 1410 | } | |||
| 1411 | else if (subs == subi) | |||
| 1412 | { | |||
| 1413 | /* got a complete SPU */ | |||
| 1414 | subi = 0; | |||
| 1415 | if (dvddecode()) | |||
| 1416 | { | |||
| 1417 | fprintf(stderrstderr, | |||
| 1418 | "found unreadable subtitle at %.2fs, skipping\n", | |||
| 1419 | (double) spts / 90000); | |||
| 1420 | continue; | |||
| 1421 | } /*if*/ | |||
| 1422 | } /*if*/ | |||
| 1423 | } /*if dump the stream*/ | |||
| 1424 | package_length = 0; | |||
| 1425 | break; | |||
| 1426 | case 0x0100 + MPID_VIDEO_FIRST: | |||
| 1427 | if (firstvideo == -1) | |||
| 1428 | { | |||
| 1429 | fread(cbuf, 1, package_length, fd.h); | |||
| 1430 | firstvideo = getpts(cbuf); | |||
| 1431 | add_offset -= firstvideo; | |||
| 1432 | package_length = 0; | |||
| 1433 | } /*if*/ | |||
| 1434 | if (debug > 5) | |||
| 1435 | fprintf(stderrstderr, "video stream 0\n"); | |||
| 1436 | break; | |||
| 1437 | case 0x01e1: | |||
| 1438 | case 0x01e2: | |||
| 1439 | case 0x01e3: | |||
| 1440 | case 0x01e4: | |||
| 1441 | case 0x01e5: | |||
| 1442 | case 0x01e6: | |||
| 1443 | case 0x01e7: | |||
| 1444 | case 0x01e8: | |||
| 1445 | case 0x01e9: | |||
| 1446 | case 0x01ea: | |||
| 1447 | case 0x01eb: | |||
| 1448 | case 0x01ec: | |||
| 1449 | case 0x01ed: | |||
| 1450 | case 0x01ee: | |||
| 1451 | case 0x01ef: | |||
| 1452 | if (debug > 5) | |||
| 1453 | fprintf(stderrstderr, "video stream %d\n", pid - 0x100 - MPID_VIDEO_FIRST); | |||
| 1454 | break; | |||
| 1455 | case 0x0100 + MPID_PAD: | |||
| 1456 | if (debug > 5) | |||
| 1457 | fprintf(stderrstderr, "padding stream %d bytes\n", package_length); | |||
| 1458 | fread(cbuf, 1, package_length, fd.h); | |||
| 1459 | if (package_length > 30) | |||
| 1460 | { | |||
| 1461 | int i; | |||
| 1462 | package_length = 0; | |||
| 1463 | i = 0; | |||
| 1464 | if (strcmp((const char *)cbuf + i, "dvdauthor-data")__extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p ((const char *)cbuf + i) && __builtin_constant_p ("dvdauthor-data" ) && (__s1_len = strlen ((const char *)cbuf + i), __s2_len = strlen ("dvdauthor-data"), (!((size_t)(const void *)(((const char *)cbuf + i) + 1) - (size_t)(const void *)((const char * )cbuf + i) == 1) || __s1_len >= 4) && (!((size_t)( const void *)(("dvdauthor-data") + 1) - (size_t)(const void * )("dvdauthor-data") == 1) || __s2_len >= 4)) ? __builtin_strcmp ((const char *)cbuf + i, "dvdauthor-data") : (__builtin_constant_p ((const char *)cbuf + i) && ((size_t)(const void *)( ((const char *)cbuf + i) + 1) - (size_t)(const void *)((const char *)cbuf + i) == 1) && (__s1_len = strlen ((const char *)cbuf + i), __s1_len < 4) ? (__builtin_constant_p ( "dvdauthor-data") && ((size_t)(const void *)(("dvdauthor-data" ) + 1) - (size_t)(const void *)("dvdauthor-data") == 1) ? __builtin_strcmp ((const char *)cbuf + i, "dvdauthor-data") : (__extension__ ( { const unsigned char *__s2 = (const unsigned char *) (const char *) ("dvdauthor-data"); int __result = (((const unsigned char *) (const char *) ((const char *)cbuf + i))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) ((const char *)cbuf + i))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) ((const char *)cbuf + i))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (( const char *)cbuf + i))[3] - __s2[3]); } } __result; }))) : ( __builtin_constant_p ("dvdauthor-data") && ((size_t)( const void *)(("dvdauthor-data") + 1) - (size_t)(const void * )("dvdauthor-data") == 1) && (__s2_len = strlen ("dvdauthor-data" ), __s2_len < 4) ? (__builtin_constant_p ((const char *)cbuf + i) && ((size_t)(const void *)(((const char *)cbuf + i) + 1) - (size_t)(const void *)((const char *)cbuf + i) == 1 ) ? __builtin_strcmp ((const char *)cbuf + i, "dvdauthor-data" ) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) ((const char *)cbuf + i); int __result = (((const unsigned char *) (const char *) ("dvdauthor-data" ))[0] - __s2[0]); if (__s2_len > 0 && __result == 0 ) { __result = (((const unsigned char *) (const char *) ("dvdauthor-data" ))[1] - __s2[1]); if (__s2_len > 1 && __result == 0 ) { __result = (((const unsigned char *) (const char *) ("dvdauthor-data" ))[2] - __s2[2]); if (__s2_len > 2 && __result == 0 ) __result = (((const unsigned char *) (const char *) ("dvdauthor-data" ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp ((const char *)cbuf + i, "dvdauthor-data")))); })) | |||
| 1465 | break; | |||
| 1466 | /* pad packet contains DVDAuthor private data */ | |||
| 1467 | i = 15; | |||
| 1468 | if (cbuf[i] != 2) | |||
| 1469 | break; | |||
| 1470 | switch(cbuf[i + 1]) | |||
| 1471 | { | |||
| 1472 | case 1: // subtitle/menu color and button information | |||
| 1473 | { | |||
| 1474 | // int st = cbuf[i + 2] & 31; // we ignore which subtitle stream for now | |||
| 1475 | struct dispdetails *buttons; | |||
| 1476 | i += 3; | |||
| 1477 | buttons = malloc(sizeof(struct dispdetails)); | |||
| 1478 | memset(buttons, 0, sizeof(struct dispdetails)); | |||
| 1479 | buttons->pts[0] = read4(cbuf + i); | |||
| 1480 | buttons->pts[1] = read4(cbuf + i + 4); | |||
| 1481 | i += 8; | |||
| 1482 | while(cbuf[i] != 0xff) | |||
| 1483 | { | |||
| 1484 | switch(cbuf[i]) | |||
| 1485 | { | |||
| 1486 | case 1: /* colour table */ | |||
| 1487 | { | |||
| 1488 | int j; | |||
| 1489 | buttons->numpal = 0; | |||
| 1490 | for (j = 0; j < cbuf[i + 1]; j++) | |||
| 1491 | { | |||
| 1492 | const int c = read4(cbuf + i + 1 + 3 * j) & 0xffffff; | |||
| 1493 | buttons->palette[j] = c; | |||
| 1494 | buttons->numpal++; | |||
| 1495 | } /*for*/ | |||
| 1496 | i += 2 + 3 * buttons->numpal; | |||
| 1497 | } | |||
| 1498 | break; | |||
| 1499 | case 2: /* button groups */ | |||
| 1500 | { | |||
| 1501 | int j; | |||
| 1502 | buttons->numcoli = cbuf[i + 1]; | |||
| 1503 | for (j = 0; j < 2 * buttons->numcoli; j++) | |||
| 1504 | buttons->coli[j] = read4(cbuf + i + 2 + j * 4); | |||
| 1505 | i += 2 + 8 * buttons->numcoli; | |||
| 1506 | } | |||
| 1507 | break; | |||
| 1508 | case 3: /* button placement */ | |||
| 1509 | { | |||
| 1510 | int j; | |||
| 1511 | buttons->numbuttons = cbuf[i + 1]; | |||
| 1512 | buttons->buttons = malloc(buttons->numbuttons * sizeof(struct button)); | |||
| 1513 | i += 2; | |||
| 1514 | for (j = 0; j < buttons->numbuttons; j++) | |||
| 1515 | { | |||
| 1516 | struct button *b = &buttons->buttons[j]; | |||
| 1517 | b->name = readpstr(cbuf, &i); | |||
| 1518 | i += 2; | |||
| 1519 | b->autoaction = cbuf[i++] != 0; | |||
| 1520 | b->grp = cbuf[i]; | |||
| 1521 | b->x1 = read2(cbuf + i + 1); | |||
| 1522 | b->y1 = read2(cbuf + i + 3); | |||
| 1523 | b->x2 = read2(cbuf + i + 5); | |||
| 1524 | b->y2 = read2(cbuf + i + 7); | |||
| 1525 | i += 9; | |||
| 1526 | // up down left right | |||
| 1527 | b->up = readpstr(cbuf, &i); | |||
| 1528 | b->down = readpstr(cbuf, &i); | |||
| 1529 | b->left = readpstr(cbuf, &i); | |||
| 1530 | b->right = readpstr(cbuf, &i); | |||
| 1531 | } /*for*/ | |||
| 1532 | } | |||
| 1533 | break; | |||
| 1534 | default: | |||
| 1535 | fprintf(stderrstderr,"ERR: unknown dvd info packet command: %d, offset %d\n",cbuf[i], i); | |||
| 1536 | exit(1); | |||
| 1537 | } /*switch*/ | |||
| 1538 | } /*while*/ | |||
| 1539 | add_pending_buttons(buttons); | |||
| 1540 | } /*case 1*/ | |||
| 1541 | break; | |||
| 1542 | } /*switch*/ | |||
| 1543 | } /*if*/ | |||
| 1544 | package_length = 0; | |||
| 1545 | break; | |||
| 1546 | case 0x01c0: | |||
| 1547 | case 0x01c1: | |||
| 1548 | case 0x01c2: | |||
| 1549 | case 0x01c3: | |||
| 1550 | case 0x01c4: | |||
| 1551 | case 0x01c5: | |||
| 1552 | case 0x01c6: | |||
| 1553 | case 0x01c7: | |||
| 1554 | case 0x01c8: | |||
| 1555 | case 0x01c9: | |||
| 1556 | case 0x01ca: | |||
| 1557 | case 0x01cb: | |||
| 1558 | case 0x01cc: | |||
| 1559 | case 0x01cd: | |||
| 1560 | case 0x01ce: | |||
| 1561 | case 0x01cf: | |||
| 1562 | case 0x01d0: | |||
| 1563 | case 0x01d1: | |||
| 1564 | case 0x01d2: | |||
| 1565 | case 0x01d3: | |||
| 1566 | case 0x01d4: | |||
| 1567 | case 0x01d5: | |||
| 1568 | case 0x01d6: | |||
| 1569 | case 0x01d7: | |||
| 1570 | case 0x01d8: | |||
| 1571 | case 0x01d9: | |||
| 1572 | case 0x01da: | |||
| 1573 | case 0x01db: | |||
| 1574 | case 0x01dc: | |||
| 1575 | case 0x01dd: | |||
| 1576 | case 0x01de: | |||
| 1577 | case 0x01df: | |||
| 1578 | if (debug > 5) | |||
| 1579 | fprintf(stderrstderr, "audio stream %d\n", pid - 0x100 - MPID_AUDIO_FIRST); | |||
| 1580 | break; | |||
| 1581 | default: | |||
| 1582 | if (debug > 0) | |||
| 1583 | fprintf(stderrstderr, "unknown header %x\n", pid); | |||
| 1584 | next_word = pid << 16 | package_length; | |||
| 1585 | package_length = 2; | |||
| 1586 | while (next_word != 0x100 + MPID_PACK) | |||
| 1587 | { | |||
| 1588 | next_word = next_word << 8; | |||
| 1589 | if (fread(&next_word, 1, 1, fd.h) < 1) | |||
| 1590 | break; | |||
| 1591 | package_length++; | |||
| 1592 | } /*while*/ | |||
| 1593 | if (debug > 0) | |||
| 1594 | fprintf(stderrstderr, "skipped %d bytes of garbage\n", package_length); | |||
| 1595 | goto l_01ba; | |||
| 1596 | } /*switch*/ | |||
| 1597 | fread(cbuf, 1, package_length, fd.h); | |||
| 1598 | } /*if*/ | |||
| 1599 | } /*if*/ | |||
| 1600 | } /*while read next packet header*/ | |||
| 1601 | varied_close(fd); | |||
| 1602 | } /*while fileindex < nrinfiles*/ | |||
| 1603 | flushspus(0x7fffffff); /* ensure all remaining spus elements are output */ | |||
| 1604 | fprintf(fdo, "\t</stream>\n</subpictures>\n"); | |||
| 1605 | fclose(fdo); | |||
| 1606 | return 0; | |||
| 1607 | } /*main*/ |