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