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*/ |