1 /*
2 dvdauthor -- generation of .VOB files
3 */
4 /*
5 * Copyright (C) 2002 Scott Smith (trckjunky@users.sourceforge.net)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301 USA.
21 */
22
23 #include "compat.h"
24
25 #include <assert.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <ctype.h>
29
30 #include "dvdauthor.h"
31 #include "da-internal.h"
32
33
34
35 struct colorremap /* for remapping colours to indexes into a common palette */
36 {
37 int newcolors[16]; /* bottom 24 bits are YCbCr, bit 24 is set to indicate a colour needs remapping */
38 int state,curoffs,maxlen,nextoffs,skip,ln_ctli; /* state of SPU parser machine (procremap) */
39 struct colorinfo *origmap; /* colours merged into common indexes into this palette */
40 };
41
42 struct vscani {
43 int lastrefsect; /* flag that last sector should be recorded as a reference sector */
44 int firstgop; /* 1 => looking for first GOP, 2 => found first GOP, 0 => don't bother looking any more */
45 int firsttemporal; /* first temporal sequence number seen in current sequence */
46 int lastadjust; /* temporal sequence reset */
47 };
48
49 static pts_t const timeline[19]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
50 20,60,120,240};
51 /* various time steps for VOBU offsets needed in DSI packet, in units of half a second */
52
53 #define BIGWRITEBUFLEN (16*2048)
54 static unsigned char bigwritebuf[BIGWRITEBUFLEN];
55 static int writebufpos=0;
56 static int writefile=-1; /* fd of output file */
57
flushclose(int fd)58 static void flushclose(int fd)
59 /* ensures all data has been successfully written to disk before closing fd. */
60 {
61 if
62 (
63 #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
64 fdatasync(fd)
65 #else
66 fsync(fd)
67 #endif
68 )
69 {
70 if (errno != EINVAL)
71 {
72 fprintf(stderr, "ERR: Error %d -- %s -- flushing output VOB\n", errno, strerror(errno));
73 exit(1);
74 }
75 else
76 {
77 /* non-flushable output */
78 errno = 0;
79 } /*if*/
80 } /*if*/
81 close(fd);
82 } /*flushclose*/
83
84 static unsigned char videoslidebuf[15]={255,255,255,255, 255,255,255, 0,0,0,0, 0,0,0,0};
85
86
87 /* The following are variants for the ways I've seen DVD's encoded */
88
89 // Grosse Pointe Blank uses exactly 1/2 second for the FF/REW records
90 // Bullitt uses 1/2 video second (i.e. in NTSC, 45045 PTS)
91 #define DVD_FFREW_HALFSEC 45000
92 // #define DVD_FFREW_HALFSEC (getratedenom(va)>>1)
93
calcpts(const struct vobgroup * va,int cancomplain,int * didcomplain,pts_t * align,pts_t basepts,int nfields)94 static pts_t calcpts
95 (
96 const struct vobgroup *va,
97 int cancomplain,
98 int *didcomplain,
99 pts_t *align,
100 pts_t basepts,
101 int nfields /* count of prior fields */
102 )
103 /* returns basepts aligned to a whole number of fields, offset by *align. */
104 {
105 // I assume pts should round down? That seems to be how mplex deals with it
106 // also see later comment
107 const pts_t fpts = getframepts(va);
108 const int bpframe = (basepts * 2 - *align + fpts / 2) / fpts; /* nearest whole field number */
109 if ((*align + bpframe * fpts) / 2 != basepts)
110 {
111 if (!*didcomplain)
112 {
113 if (cancomplain)
114 fprintf(stderr, "WARN: Video PTS does not line up on a multiple of a field.\n");
115 *didcomplain = 1;
116 } /*if*/
117 *align = basepts * 2; /* assume this will avoid further warnings */
118 }
119 else
120 nfields += bpframe;
121 return (*align + nfields * fpts) / 2;
122 } /*calcpts*/
123
findnextvideo(const struct vob * va,int cur,int dir)124 static int findnextvideo(const struct vob *va, int cur, int dir)
125 // find next (dir=1) or previous(dir=-1) vobu with video
126 {
127 int i, numvobus;
128 numvobus = va->numvobus;
129 switch(dir)
130 {
131 case 1: // forward
132 for (i = cur+1; i < numvobus; i++)
133 if(va->vobu[i].hasvideo)
134 return i;
135 return -1;
136 case -1: // backward
137 for (i = cur-1; i > -1; i--)
138 if(va->vobu[i].hasvideo)
139 return i;
140 return -1;
141 default:
142 // ??
143 return -1;
144 } /*switch*/
145 } /*findnextvideo*/
146
findaudsect(const struct vob * va,int aind,pts_t pts0,pts_t pts1)147 static int findaudsect(const struct vob *va, int aind, pts_t pts0, pts_t pts1)
148 /* finds the audpts entry, starting from aind, that includes the time pts0 .. pts1,
149 or -1 if not found. */
150 {
151 const struct audchannel * const ach = &va->audch[aind];
152 int l = 0, h = ach->numaudpts - 1;
153
154 if (h < l)
155 return -1;
156 while (h > l)
157 {
158 const int m =(l + h + 1) / 2; /* binary search */
159 if (pts0 < ach->audpts[m].pts[0])
160 h = m - 1;
161 else
162 l = m;
163 } /*while*/
164 if (ach->audpts[l].pts[0] > pts1)
165 return -1;
166 return ach->audpts[l].asect;
167 } /*findaudsect*/
168
findvobubysect(const struct vob * va,int sect)169 static int findvobubysect(const struct vob *va, int sect)
170 /* returns the index of the VOBU that spans the specified sector. */
171 {
172 int l = 0, h = va->numvobus - 1;
173 if (h < 0)
174 return -1;
175 if (sect < va->vobu[0].sector)
176 return -1;
177 while (l < h)
178 {
179 const int m = (l + h + 1) / 2; /* binary search */
180 if (sect < va->vobu[m].sector)
181 h = m - 1;
182 else
183 l = m;
184 } /*while*/
185 return l;
186 } /*findvobubysect*/
187
findspuidx(const struct vob * va,int ach,pts_t pts0)188 static int findspuidx(const struct vob *va, int ach, pts_t pts0)
189 /* returns the index of the subpicture packet spanning the specified time. */
190 {
191 int l = 0, h = va->audch[ach].numaudpts - 1;
192 if (h < l)
193 return -1;
194 while (h > l)
195 {
196 const int m = (l + h + 1) / 2; /* binary search */
197 if (pts0 < va->audch[ach].audpts[m].pts[0])
198 h = m - 1;
199 else
200 l = m;
201 } /*while*/
202 return l;
203 } /*findspuidx*/
204
getsect(const struct vob * va,int curvobunum,int jumpvobunum,int skip,unsigned notfound)205 static unsigned int getsect
206 (
207 const struct vob * va,
208 int curvobunum, /* the VOBU number I'm jumping from */
209 int jumpvobunum, /* the VOBU number I'm jumping to */
210 int skip, /* whether to set the skipping-more-than-one-VOBU bit */
211 unsigned notfound /* what to return if there is no matching VOBU */
212 )
213 /* computes relative VOBU offsets needed at various points in a DSI packet,
214 including the mask bit that indicates a backward jump, and optionally the
215 one indicating skipping multiple video VOBUs as well. */
216 {
217 if (skip)
218 {
219 int l, h, i;
220 // only set skip bit if one of the VOBU's from here to there contain video
221 /* hmm, this page <http://www.mpucoder.com/DVD/dsi_pkt.html> doesn't say
222 it has to contain video */
223 if (curvobunum < jumpvobunum)
224 {
225 l = curvobunum + 1;
226 h = jumpvobunum - 1;
227 }
228 else
229 {
230 l = jumpvobunum + 1;
231 h = curvobunum - 1;
232 } /*if*/
233 for (i = l; i <= h; i++)
234 if (va->vobu[i].hasvideo)
235 break; /* found an in-between VOBU containing video */
236 if (i <= h)
237 skip = 0x40000000;
238 else
239 skip = 0;
240 } /*if*/
241 if
242 (
243 jumpvobunum < 0
244 ||
245 jumpvobunum >= va->numvobus
246 ||
247 va->vobu[jumpvobunum].vobcellid != va->vobu[curvobunum].vobcellid
248 /* never cross cells */
249 )
250 return
251 notfound | skip;
252 return
253 abs(va->vobu[jumpvobunum].sector - va->vobu[curvobunum].sector)
254 |
255 (va->vobu[jumpvobunum].hasvideo ? 0x80000000 : 0)
256 |
257 skip;
258 } /*getsect*/
259
readscr(const unsigned char * buf)260 static pts_t readscr(const unsigned char *buf)
261 /* returns the timestamp as found in the pack header. This is actually supposed to
262 be units of a 27MHz clock, but I ignore the extra precision and truncate it to
263 the usual 90kHz clock units. */
264 {
265 return
266 ((pts_t)(buf[0] & 0x38)) << 27 /* SCR 32 .. 30 */
267 |
268 (buf[0] & 3) << 28 /* SCR 29 .. 28 */
269 |
270 buf[1] << 20 /* SCR 27 .. 20 */
271 |
272 (buf[2] & 0xf8) << 12 /* SCR 19 .. 15 */
273 |
274 (buf[2] & 3) << 13 /* SCR 14 .. 13 */
275 |
276 buf[3] << 5 /* SCR 12 .. 5 */
277 |
278 (buf[4] & 0xf8) >> 3; /* SCR 4 .. 0 */
279 /* ignore SCR_ext */
280 } /*readscr*/
281
writescr(unsigned char * buf,pts_t scr)282 static void writescr(unsigned char *buf, pts_t scr)
283 /* writes a new timestamp for a pack header, ignoring the additional 27MHz
284 precision. */
285 {
286 buf[0] = ((scr >> 27) & 0x38) | ((scr >> 28) & 3) | 68;
287 buf[1] = scr >> 20;
288 buf[2] = ((scr >> 12) & 0xf8) | ((scr >> 13) & 3) | 4;
289 buf[3] = scr >> 5;
290 buf[4] = ((scr << 3) & 0xf8) | (buf[4] & 7);
291 } /*writescr*/
292
readpts(const unsigned char * buf)293 static pts_t readpts(const unsigned char *buf)
294 /* reads a timestamp from a PES header as expressed in 90kHz clock units. */
295 {
296 int a1, a2, a3;
297 a1 = (buf[0] & 0xe) >> 1;
298 a2 = ((buf[1] << 8) | buf[2]) >> 1;
299 a3 = ((buf[3] << 8) | buf[4]) >> 1;
300 return
301 ((pts_t)a1) << 30
302 |
303 a2 << 15
304 |
305 a3;
306 } /*readpts*/
307
writepts(unsigned char * buf,pts_t pts)308 static void writepts(unsigned char *buf, pts_t pts)
309 /* writes a timestamp to a PES header as expressed in 90kHz clock units. */
310 {
311 buf[0] = ((pts >> 29) & 0xe) | (buf[0] & 0xf1);
312 // this preserves the PTS / DTS / PTSwDTS top bits
313 write2(buf + 1, (pts >> 14) | 1);
314 write2(buf + 3, (pts << 1) | 1);
315 } /*writepts*/
316
findbutton(const struct pgc * pg,const char * dest,int dflt)317 static int findbutton(const struct pgc *pg, const char *dest, int dflt)
318 /* returns the index of the button with name dest, or dflt if not found or no name specified. */
319 {
320 int i;
321 if (!dest)
322 return dflt;
323 for (i = 0; i < pg->numbuttons; i++)
324 if (!strcmp(pg->buttons[i].name,dest))
325 return i + 1;
326 return dflt;
327 } /*findbutton*/
328
transpose_ts(unsigned char * buf,pts_t tsoffs)329 static void transpose_ts(unsigned char *buf, pts_t tsoffs)
330 /* adjusts the timestamps in the specified PACK header and its constituent packets
331 by the specified amount. */
332 {
333 // pack scr
334 if
335 (
336 buf[0] == 0
337 &&
338 buf[1] == 0
339 &&
340 buf[2] == 1
341 &&
342 buf[3] == MPID_PACK
343 )
344 {
345 const int sysoffs =
346 buf[14] == 0 && buf[15] == 0 && buf[16] == 1 && buf[17] == MPID_SYSTEM ?
347 /* skip system header if present */
348 (buf[18] << 8 | buf[19]) + 6
349 :
350 0;
351 writescr(buf + 4, readscr(buf + 4) + tsoffs);
352 // video/audio?
353 // pts?
354 if
355 (
356 buf[14 + sysoffs] == 0
357 &&
358 buf[15 + sysoffs] == 0
359 &&
360 buf[16 + sysoffs] == 1
361 &&
362 (
363 buf[17 + sysoffs] == MPID_PRIVATE1
364 /* audio or subpicture stream */
365 ||
366 buf[17 + sysoffs] >= MPID_AUDIO_FIRST && buf[17 + sysoffs] <= MPID_VIDEO_LAST
367 /* audio or video stream */
368 )
369 &&
370 (buf[21 + sysoffs] & 128) /* PTS present */
371 )
372 {
373 writepts(buf + 23 + sysoffs, readpts(buf + 23 + sysoffs) + tsoffs);
374 // dts?
375 if (buf[21 + sysoffs] & 64) /* decoder timestamp present */
376 {
377 writepts(buf + 28 + sysoffs, readpts(buf + 28 + sysoffs) + tsoffs);
378 } /*if*/
379 } /*if*/
380 }
381 } /*transpose_ts*/
382
has_gop(const unsigned char * buf)383 static bool has_gop(const unsigned char *buf)
384 /* returns true iff there is a video GOP present in the sector in buf. */
385 {
386 if
387 (
388 buf[14] == 0
389 &&
390 buf[15] == 0
391 &&
392 buf[16] == 1
393 &&
394 buf[17] == MPID_SYSTEM
395 &&
396 buf[38] == 0
397 &&
398 buf[39] == 0
399 &&
400 buf[40] == 1
401 &&
402 buf[41] == MPID_VIDEO_FIRST
403 )
404 {
405 int i = 42;
406 while (i < 1024)
407 {
408 if
409 (
410 buf[i] == 0
411 &&
412 buf[i + 1] == 0
413 &&
414 buf[i + 2] == 1
415 &&
416 buf[i + 3] == MPID_GOP
417 )
418 return true;
419 i += 4;
420 } /*while*/
421 } /*if*/
422 return false;
423 } /*has_gop*/
424
mpa_valid(const unsigned char * b)425 static bool mpa_valid(const unsigned char *b)
426 /* does b look like it points at a valid MPEG audio packet header. */
427 {
428 const unsigned int v = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
429 int t;
430 // sync, mpeg1, layer2, 48khz
431 if ((v & 0xFFFE0C00) != 0xFFFC0400)
432 return false;
433 // bitrate 1..14
434 t = (v >> 12) & 15;
435 if (t == 0 || t == 15)
436 return false;
437 // emphasis reserved
438 if ((v & 3) == 2)
439 return false;
440 return true;
441 } /*mpa_valid*/
442
mpa_len(const unsigned char * b)443 static int mpa_len(const unsigned char *b)
444 /* returns the length of an MPEG audio packet. */
445 {
446 static int const bitratetable[16]={0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0};
447 const int padding =(b[2] >> 1) & 1;
448 const int bitrate = bitratetable[(b[2] >> 4) & 15];
449 return 3 * bitrate + padding; // 144 * bitrate / sampling; 144 / 48 = 3
450 } /*mpa_len*/
451
writeflush()452 static void writeflush()
453 /* writes out the data buffered so far. */
454 {
455 if (!writebufpos) /* nothing in buffer */
456 return;
457 if (writefile != -1)
458 {
459 if (write(writefile, bigwritebuf, writebufpos) != writebufpos)
460 {
461 fprintf(stderr, "ERR: Error %d -- %s -- writing data\n", errno, strerror(errno));
462 exit(1);
463 } /*if*/
464 } /*if*/
465 writebufpos = 0;
466 } /*writeflush*/
467
writegrabbuf()468 static unsigned char *writegrabbuf()
469 /* returns the start address at which to write the next sector,
470 automatically flushing previously-written sectors as necessary. */
471 {
472 unsigned char *buf;
473 if (writebufpos == BIGWRITEBUFLEN)
474 writeflush();
475 buf=bigwritebuf+writebufpos;
476 writebufpos += 2048; /* sector will be written to output file */
477 return buf;
478 } /*writegrabbuf*/
479
writeundo()480 static void writeundo()
481 /* drops the last sector from the output buffer. */
482 {
483 writebufpos -= 2048;
484 } /*writeundo*/
485
writeclose()486 static void writeclose()
487 /* flushes and closes the output file. */
488 {
489 writeflush();
490 if (writefile != -1)
491 {
492 flushclose(writefile);
493 writefile = -1;
494 } /*if*/
495 } /*writeclose*/
496
writeopen(const char * newname)497 static void writeopen(const char *newname)
498 /* opens an output file for writing. */
499 {
500 writefile = open(newname, O_CREAT | O_WRONLY | O_BINARY, 0666);
501 if (writefile < 0)
502 {
503 fprintf(stderr, "ERR: Error %d opening %s: %s\n", errno, newname, strerror(errno));
504 exit(1);
505 } /*if*/
506 }
507
closelastref(struct vobuinfo * thisvi,struct vscani * vsi,int cursect)508 static void closelastref(struct vobuinfo *thisvi, struct vscani *vsi, int cursect)
509 /* collects another end-sector of another reference frame, if I don't have enough already. */
510 {
511 if (vsi->lastrefsect && thisvi->numref < 3)
512 {
513 thisvi->lastrefsect[thisvi->numref++] = cursect;
514 vsi->lastrefsect = 0;
515 } /*if*/
516 } /*closelastref*/
517
518 // this function is allowed to update buf[7] and guarantee it will end up
519 // in the output stream
520 // prevbytesect is the sector for the byte immediately preceding buf[0]
scanvideoptr(struct vobgroup * va,unsigned char * buf,struct vobuinfo * thisvi,int prevbytesect,struct vscani * vsi)521 static void scanvideoptr
522 (
523 struct vobgroup *va,
524 unsigned char *buf,
525 struct vobuinfo *thisvi,
526 int prevbytesect,
527 struct vscani *vsi
528 )
529 {
530 if
531 (
532 buf[0] == 0
533 &&
534 buf[1] == 0
535 &&
536 buf[2] == 1 /* looks like a packet header */
537 )
538 {
539 switch(buf[3])
540 {
541 case MPID_PICTURE: /* picture header */
542 {
543 const int ptype = (buf[5] >> 3) & 7; /* frame type, 1 => I, 2 => P, 3 => B, 4 => D */
544 const int temporal = (buf[4] << 2) | (buf[5] >> 6); /* temporal sequence number */
545 closelastref(thisvi, vsi, prevbytesect);
546 if (vsi->firsttemporal == -1)
547 vsi->firsttemporal = temporal;
548 vsi->lastadjust = (temporal < vsi->firsttemporal);
549 if (ptype == 1 || ptype == 2) // I or P frame
550 vsi->lastrefsect = 1; /* it's a reference frame */
551 if (va->vd.vmpeg == VM_MPEG1)
552 {
553 thisvi->numfields += 2;
554 if (vsi->lastadjust && vsi->firstgop == 2)
555 thisvi->firstIfield += 2;
556 } /*if*/
557
558 // fprintf(stderr,"INFO: frame type %d, tempref=%d, prevsect=%d\n",ptype,temporal,prevbytesect);
559 break;
560 } /*case 0*/
561
562 case MPID_SEQUENCE: /* sequence header */
563 {
564 /* collect information about video attributes */
565 int hsize, vsize, aspect, framerate, newaspect;
566 char sizestring[30];
567 closelastref(thisvi, vsi, prevbytesect);
568 hsize = (buf[4] << 4) | (buf[5] >> 4);
569 vsize = ((buf[5] << 8) & 0xf00) | buf[6];
570 aspect = buf[7] >> 4;
571 framerate = buf[7] & 0xf;
572
573 vobgroup_set_video_framerate(va, framerate);
574 switch (framerate)
575 {
576 case VR_NTSCFILM: // 24000/1001
577 case VR_NTSC: // 30000/1001
578 case VR_NTSCFIELD: // 60000/1001
579 vobgroup_set_video_attr(va, VIDEO_FORMAT, "ntsc");
580 break;
581
582 case VR_PAL: // 25
583 case VR_PALFIELD: // 50
584 vobgroup_set_video_attr(va, VIDEO_FORMAT, "pal");
585 break;
586
587 case VR_FILM: // 24
588 case VR_30: // 30
589 case VR_60: // 60
590 // these are nonstandard, but at least we know what they are
591 break;
592
593 default:
594 fprintf(stderr, "WARN: unknown frame rate %d\n", framerate);
595 break;
596 } /* switch(framerate) */
597 sprintf(sizestring, "%dx%d", hsize, vsize);
598 vobgroup_set_video_attr(va, VIDEO_RESOLUTION, sizestring);
599 if (va->vd.vmpeg == VM_MPEG1)
600 {
601 switch (aspect)
602 {
603 case 3:
604 vobgroup_set_video_attr(va, VIDEO_ASPECT, "16:9");
605 vobgroup_set_video_attr(va, VIDEO_FORMAT, "pal");
606 break;
607 case 6:
608 vobgroup_set_video_attr(va, VIDEO_ASPECT, "16:9");
609 vobgroup_set_video_attr(va, VIDEO_FORMAT, "ntsc");
610 break;
611 case 8:
612 vobgroup_set_video_attr(va, VIDEO_ASPECT, "4:3");
613 vobgroup_set_video_attr(va, VIDEO_FORMAT, "pal");
614 break;
615 case 12:
616 vobgroup_set_video_attr(va, VIDEO_ASPECT, "4:3");
617 vobgroup_set_video_attr(va, VIDEO_FORMAT, "ntsc");
618 break;
619 default:
620 fprintf(stderr,"WARN: unknown mpeg1 aspect ratio %d\n",aspect);
621 break;
622 } /*switch*/
623 newaspect =
624 3
625 +
626 (va->vd.vaspect == VA_4x3) * 5
627 +
628 (va->vd.vformat == VF_NTSC) * 3;
629 if (newaspect == 11)
630 newaspect++;
631 buf[7] = (buf[7] & 0xf) | (newaspect << 4); // reset the aspect ratio
632 }
633 else if (va->vd.vmpeg == VM_MPEG2)
634 {
635 if (aspect == 2)
636 vobgroup_set_video_attr(va, VIDEO_ASPECT, "4:3");
637 else if (aspect == 3)
638 vobgroup_set_video_attr(va, VIDEO_ASPECT, "16:9");
639 else
640 fprintf(stderr, "WARN: unknown mpeg2 aspect ratio %d\n", aspect);
641 buf[7] = (buf[7] & 0xf) | (va->vd.vaspect == VA_4x3 ? 2 : 3) << 4;
642 // reset the aspect ratio
643 } /*if*/
644 break;
645 } /* case MPID_SEQUENCE */
646
647 case MPID_EXTENSION: /* extension header */
648 {
649 vobgroup_set_video_attr(va, VIDEO_MPEG, "mpeg2");
650 switch (buf[4] & 0xF0)
651 {
652 case 0x10: // sequence extension
653 closelastref(thisvi, vsi, prevbytesect);
654 break;
655
656 case 0x20: // sequence display extension
657 closelastref(thisvi, vsi, prevbytesect);
658 switch (buf[4] & 0xE) /* video format */
659 {
660 case 2:
661 vobgroup_set_video_attr(va, VIDEO_FORMAT, "pal");
662 break;
663 case 4:
664 vobgroup_set_video_attr(va, VIDEO_FORMAT, "ntsc");
665 break;
666 // case 6: // secam
667 // case 10: // unspecified
668 }
669 break;
670
671 case 0x80: // picture coding extension
672 {
673 int padj = 1; // default field pic
674 if ((buf[6] & 3) /* picture structure */ == 3)
675 padj++; // adj for frame pic
676 if (buf[7] & 2) /* repeat first field */
677 padj++; // adj for repeat flag
678 thisvi->numfields += padj;
679 if (vsi->lastadjust && vsi->firstgop == 2)
680 thisvi->firstIfield += padj;
681 // fprintf(stderr,"INFO: repeat flag=%d, cursect=%d\n",buf[7]&2,cursect);
682 }
683 break;
684 } /*switch*/
685 break;
686 } /* case MPID_EXTENSION */
687
688 case MPID_SEQUENCE_END:
689 thisvi->hasseqend = 1;
690 break;
691
692 case MPID_GOP: // gop header
693 closelastref(thisvi, vsi, prevbytesect);
694 if (vsi->firstgop == 1)
695 {
696 vsi->firstgop = 2; /* found first GOP */
697 vsi->firsttemporal = -1;
698 vsi->lastadjust = 0;
699 }
700 else if (vsi->firstgop == 2)
701 {
702 vsi->firstgop = 0; /* no need to find any more GOPs */
703 } /*if*/
704 if (false)
705 {
706 int hr, mi, se, fr;
707 hr = (buf[4] >> 2) & 31;
708 mi = ((buf[4] & 3) << 4) | (buf[5] >> 4);
709 se = ((buf[5] & 7) << 3) | (buf[6] >> 5);
710 fr = ((buf[6] & 31) << 1) | (buf[7] >> 7);
711 fprintf
712 (
713 stderr,
714 "INFO: GOP header, %d:%02d:%02d:%02d, drop=%d\n",
715 hr, mi, se, fr, buf[4] >> 7
716 );
717 } /*if*/
718 break; /* case MPID_GOP */
719 } /*switch*/
720 } /*if*/
721 } /*scanvideoptr*/
722
scanvideoframe(struct vobgroup * va,unsigned char * buf,struct vobuinfo * thisvi,int cursect,int prevsect,struct vscani * vsi)723 static void scanvideoframe(struct vobgroup *va, unsigned char *buf, struct vobuinfo *thisvi, int cursect, int prevsect, struct vscani *vsi)
724 {
725 int i, f = 0x17 + buf[0x16], l = 0x14 + buf[0x12] * 256 + buf[0x13];
726 int mpf;
727 struct vobuinfo oldtvi;
728 struct vscani oldvsi;
729 if (l - f < 8)
730 {
731 memcpy(videoslidebuf + 7, buf + f, l - f);
732 for (i = 0; i < l - f; i++)
733 scanvideoptr(va, videoslidebuf + i, thisvi, prevsect, vsi);
734 memcpy(buf + f, videoslidebuf + 7, l - f);
735 memset(videoslidebuf, 255, 7);
736 return;
737 } /*if*/
738 rescan:
739 mpf = va->vd.vmpeg;
740 oldtvi = *thisvi;
741 oldvsi = *vsi;
742 // copy the first 7 bytes to use with the prev 7 bytes in hdr detection
743 memcpy(videoslidebuf + 7, buf + f, 8); // we scan the first header using the slide buffer
744 for (i = 0; i <= 7; i++)
745 scanvideoptr(va, videoslidebuf + i, thisvi, prevsect, vsi);
746 memcpy(buf + f, videoslidebuf + 7, 8);
747 // quickly scan all but the last 7 bytes for a hdr
748 // buf[f]... was already scanned in the videoslidebuffer to give the correct sector
749 for (i = f + 1; i < l - 7; i++)
750 {
751 if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1)
752 scanvideoptr(va, buf + i, thisvi, cursect, vsi);
753 } /*for*/
754 if (!va->vd.vmpeg)
755 vobgroup_set_video_attr(va, VIDEO_MPEG, "mpeg1");
756 // if the mpeg version changed, then rerun scanvideoframe, because
757 // scanvideoptr updates the aspect ratio in the sequence header
758 if (mpf != va->vd.vmpeg)
759 {
760 *thisvi = oldtvi; // we must undo all the frame pointer changes
761 *vsi = oldvsi;
762 goto rescan;
763 } /*if*/
764 // use the last 7 bytes in the next iteration
765 memcpy(videoslidebuf, buf + l - 7, 7);
766 } /*scanvideoframe*/
767
finishvideoscan(struct vobgroup * va,int vob,int prevsect,struct vscani * vsi)768 static void finishvideoscan(struct vobgroup *va, int vob, int prevsect, struct vscani *vsi)
769 {
770 struct vobuinfo * const lastvi = &va->vobs[vob]->vobu[va->vobs[vob]->numvobus - 1];
771 int i;
772 memset(videoslidebuf + 7, 0, 7);
773 for (i = 0; i < 7; i++)
774 scanvideoptr(va, videoslidebuf + i, lastvi, prevsect, vsi);
775 memset(videoslidebuf, 255, 7);
776 closelastref(lastvi, vsi, prevsect);
777 } /*finishvideoscan*/
778
printpts(pts_t pts)779 static void printpts(pts_t pts)
780 /* displays a PTS value in seconds. */
781 {
782 fprintf(stderr,"%d.%03d",(int)(pts/90000),(int)((pts/90)%1000));
783 }
784
785 enum /* states for SPU parser */
786 {
787 CR_BEGIN0, CR_BEGIN1, CR_BEGIN2, CR_BEGIN3, CR_SKIP0,
788 CR_SKIP1, CR_NEXTOFFS0, CR_NEXTOFFS1, CR_WAIT, CR_CMD,
789 CR_SKIPWAIT, CR_COL0, CR_COL1, CR_CHGARG, CR_CHGLN0,
790 CR_CHGLN1, CR_CHGLN2, CR_CHGLN3, CR_CHGPX0, CR_CHGPX1,
791 CR_CHGPX2,
792 };
793
readpstr(const unsigned char * b,int * i)794 static char *readpstr(const unsigned char *b, int *i)
795 /* extracts a null-terminated string beginning at b[*i], advances *i past it and returns
796 a copy of the string. */
797 {
798 char *s = strdup((const char *)b + i[0]);
799 i[0] += strlen(s) + 1;
800 return s;
801 } /*readpstr*/
802
initremap(struct colorremap * cr)803 static void initremap(struct colorremap *cr)
804 /* initializes the remap table to all identity mappings, and the state machine
805 ready to start processing a new SPU. */
806 {
807 int i;
808 for (i = 0; i < 16; i++) /* initially don't remap any colours */
809 cr->newcolors[i] = i;
810 cr->state = CR_BEGIN0;
811 cr->origmap = 0;
812 } /*initremap*/
813
remapcolor(struct colorremap * cr,int idx)814 static int remapcolor(struct colorremap *cr, int idx)
815 /* returns the appropriate remapping of the colour with the specified index
816 to the corresponding index in cr->origmap. */
817 {
818 int i, nc;
819 if (cr->newcolors[idx] < 16)
820 return
821 cr->newcolors[idx]; /* remapping already worked out */
822 nc = cr->newcolors[idx] & 0xffffff; /* get colour to be remapped */
823 for (i = 0; i < 16; i++) /* find existing entry, if any */
824 if (cr->origmap->color[i] == nc) /* got one */
825 {
826 cr->newcolors[idx] = i;
827 return
828 i;
829 } /*if; for*/
830 for (i = 0; i < 16; i++)
831 /* allocate a new entry in origmap for it, if there is room */
832 if (cr->origmap->color[i] == COLOR_UNUSED)
833 {
834 cr->origmap->color[i] = nc;
835 cr->newcolors[idx] = i;
836 return
837 i;
838 } /*if; for */
839 fprintf(stderr, "ERR: color map full, unable to allocate new colors.\n");
840 exit(1);
841 } /*remapcolor*/
842
remapbyte(struct colorremap * cr,unsigned char * b)843 static void remapbyte(struct colorremap *cr, unsigned char *b)
844 /* remaps the two colours in the two nibbles of *b. */
845 {
846 b[0] = remapcolor(cr, b[0] & 15) | (remapcolor(cr, b[0] >> 4) << 4);
847 } /*remapbyte*/
848
procremap(struct colorremap * cr,unsigned char * b,int blen,pts_t * timespan)849 static void procremap
850 (
851 struct colorremap *cr,
852 unsigned char *b,
853 int blen, /* length of SPU chunk beginning at b */
854 pts_t *timespan /* has duration of SPU display added to it */
855 )
856 /* interprets the subpicture stream in order to pick up the colour information so it
857 can be remapped. */
858 {
859 while(blen)
860 {
861 // fprintf(stderr,"INFO: state=%d, byte=%02x (%d)\n",cr->state,*b,*b);
862 switch(cr->state)
863 {
864 case CR_BEGIN0: /* pick up high byte of Sub-Picture Unit length */
865 cr->curoffs = 0;
866 cr->skip = 0;
867 cr->maxlen = b[0] * 256;
868 cr->state = CR_BEGIN1;
869 break;
870 case CR_BEGIN1: /* pick up low byte of Sub-Picture Unit length */
871 cr->maxlen += b[0];
872 cr->state = CR_BEGIN2;
873 break;
874 case CR_BEGIN2: /* pick up high byte of offset to SP_DCSQT */
875 cr->nextoffs = b[0] * 256;
876 cr->state = CR_BEGIN3;
877 break;
878 case CR_BEGIN3: /* pick up low byte of offset to SP_DCSQT */
879 cr->nextoffs += b[0];
880 cr->state = CR_WAIT;
881 break;
882 case CR_WAIT: /* skipping bytes until start of SP_DCSQT */
883 if (cr->curoffs == cr->maxlen)
884 {
885 cr->state = CR_BEGIN0; /* finished this SPU */
886 continue; // execute BEGIN0 right away
887 } /*if*/
888 if (cr->curoffs != cr->nextoffs)
889 break;
890 cr->state = CR_SKIP0; /* found start of SP_DCSQT */
891 // fall through to CR_SKIP0
892 case CR_SKIP0: /* pick up high byte of SP_DCSQ_TM */
893 *timespan += 1024 * b[0] * 256;
894 cr->state = CR_SKIP1;
895 break;
896 case CR_SKIP1: /* pick up low byte of SP_DCSQ_TM */
897 *timespan += 1024 * b[0];
898 cr->state = CR_NEXTOFFS0;
899 break;
900 case CR_NEXTOFFS0: /* pick up high byte of offset to next SP_DCSQ */
901 cr->nextoffs = b[0] * 256;
902 cr->state = CR_NEXTOFFS1;
903 break;
904 case CR_NEXTOFFS1: /* pick up low byte of offset to next SP_DCSQ */
905 cr->nextoffs += b[0];
906 cr->state = CR_CMD; /* expecting first command */
907 break;
908 case CR_SKIPWAIT: /* skipping rest of current command */
909 if (cr->skip)
910 {
911 cr->skip--;
912 break;
913 } /*if*/
914 cr->state = CR_CMD;
915 // fall through to CR_CMD
916 case CR_CMD: /* pick up start of next command */
917 switch (*b)
918 {
919 case SPU_FSTA_DSP:
920 case SPU_STA_DSP:
921 case SPU_STP_DSP:
922 /* nothing to do */
923 break;
924 case SPU_SET_COLOR:
925 cr->state = CR_COL0;
926 break;
927 case SPU_SET_CONTR:
928 cr->skip = 2; /* no need to look at this */
929 cr->state = CR_SKIPWAIT;
930 break;
931 case SPU_SET_DAREA:
932 cr->skip = 6; /* no need to look at this */
933 cr->state = CR_SKIPWAIT;
934 break;
935 case SPU_SET_DSPXA:
936 cr->skip = 4; /* no need to look at this */
937 cr->state = CR_SKIPWAIT;
938 break;
939 case SPU_CHG_COLCON:
940 cr->skip = 2; /* skip size of parameter area */
941 cr->state = CR_CHGARG;
942 break;
943 case SPU_CMD_END:
944 cr->state = CR_WAIT; /* end of SP_DCSQ */
945 break;
946 default:
947 fprintf(stderr, "ERR: procremap encountered unknown subtitle command: %d\n",*b);
948 exit(1);
949 } /*switch*/
950 break;
951 case CR_COL0: /* first and second of four colours for SET_COLOR */
952 remapbyte(cr, b);
953 cr->state = CR_COL1;
954 break;
955 case CR_COL1: /* third and fourth of four colours for SET_COLOR */
956 remapbyte(cr, b);
957 cr->state = CR_CMD;
958 break;
959 case CR_CHGARG: /* skipping size word for CHG_COLCON */
960 if (!--cr->skip)
961 cr->state = CR_CHGLN0;
962 break;
963 case CR_CHGLN0: /* expecting first byte of LN_CTLI subcommand for CHG_COLCON */
964 cr->ln_ctli = b[0] << 24;
965 cr->state = CR_CHGLN1;
966 break;
967 case CR_CHGLN1:
968 cr->ln_ctli |= b[0] << 16;
969 cr->state = CR_CHGLN2;
970 break;
971 case CR_CHGLN2:
972 cr->ln_ctli |= b[0] << 8;
973 cr->state = CR_CHGLN3;
974 break;
975 case CR_CHGLN3:
976 cr->ln_ctli |= b[0]; /* got complete four bytes at start of LN_CTLI */
977 if (cr->ln_ctli == 0x0fffffff) /* end of CHG_COLCON parameter area */
978 cr->state = CR_CMD;
979 else
980 {
981 cr->ln_ctli >>= 12;
982 cr->ln_ctli &= 0xf; /* number of PX_CTLI to follow */
983 cr->skip = 2;
984 cr->state = CR_CHGPX0;
985 } /*if*/
986 break;
987 case CR_CHGPX0: /* expecting starting column nr for PX_CTLI subcommand for CHG_COLCON */
988 if (!--cr->skip)
989 {
990 cr->skip = 2;
991 cr->state = CR_CHGPX1;
992 } /*if*/
993 break;
994 case CR_CHGPX1: /* expecting new colour values for PX_CTLI subcommand */
995 remapbyte(cr, b);
996 if (!--cr->skip)
997 {
998 cr->skip = 2;
999 cr->state = CR_CHGPX2;
1000 } /*if*/
1001 break;
1002 case CR_CHGPX2: /* expecting new contrast values for PX_CTLI subcommand */
1003 if (!--cr->skip)
1004 {
1005 /* done this PX_CTLI */
1006 if (!--cr->ln_ctli)
1007 cr->state = CR_CHGLN0; /* done this LN_CTLI */
1008 else
1009 {
1010 cr->skip = 2;
1011 cr->state = CR_CHGPX0; /* next PX_CTLI in this LN_CTLI */
1012 } /*if*/
1013 } /*if*/
1014 break;
1015 default: /* shouldn't occur */
1016 assert(false);
1017 } /*switch*/
1018 cr->curoffs++;
1019 b++;
1020 blen--;
1021 } /*while*/
1022 } /*procremap*/
1023
printvobustatus(struct vobgroup * va,int cursect,bool checknonempty)1024 static void printvobustatus(struct vobgroup *va, int cursect, bool checknonempty)
1025 /* report total number of VOBUs and PGCs seen so far, and how much of the
1026 input file has been processed. */
1027 {
1028 int j, nv = 0;
1029 for (j = 0; j < va->numvobs; j++)
1030 nv += va->vobs[j]->numvobus;
1031 // fprintf(stderr, "STAT: VOBU %d at %dMB, %d PGCs, %d:%02d:%02d\r", nv, cursect / 512, va->numallpgcs, total / 324000000, (total % 324000000) / 5400000, (total % 5400000) / 90000);
1032 fprintf(stderr, "STAT: VOBU %d at %dMB, %d PGCs\r", nv, cursect / 512, va->numallpgcs);
1033 if (checknonempty && nv == 0)
1034 {
1035 fprintf(stderr, "\nERR: no VOBUs found.\n");
1036 exit(1);
1037 } /*if*/
1038 } /*printvobustatus*/
1039
audio_scan_ac3(struct audchannel * ach,const unsigned char * buf,int sof,int len)1040 static void audio_scan_ac3(struct audchannel *ach, const unsigned char *buf, int sof, int len)
1041 /* gets information about AC3 audio. */
1042 {
1043 uint32_t parse;
1044 int acmod,lfeon,nch=0;
1045 char attr[4];
1046
1047 if( sof+8>len ) // check if there's room to parse all the interesting info
1048 return;
1049 if( buf[sof]!=0x0b || buf[sof+1]!=0x77 ) // verify ac3 sync
1050 return;
1051 parse=read4(buf+sof+4);
1052 if( (parse>>30)!=0 ) { // must be 48kHz
1053 fprintf(stderr,"WARN: Unknown AC3 sample rate: %d\n",parse>>30);
1054 }
1055 audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_SAMPLERATE,"48khz");
1056 /* now figure out how many channels are present ... (yes, all this code) */
1057 parse<<=8;
1058 // check bsid
1059 if( (parse>>27)!=8 && (parse>>27)!=6 ) // must be v6 or v8
1060 return;
1061 parse<<=5;
1062 // ignore bsmod
1063 parse<<=3;
1064 // acmod gives # of channels
1065 acmod=(parse>>29);
1066 parse<<=3;
1067 if( (acmod&1) && (acmod!=1) ) /* centre channel present */
1068 parse<<=2; /* skip cmixlev */
1069 if( acmod&4 ) /* surround channel(s) present */
1070 parse<<=2; /* skip surmixlev */
1071 if( acmod==2 ) { /* simple stereo */
1072 /* process dsurmod */
1073 if( (parse>>30)==2 ) /* 2 => Dolby Surround encoded, 1 => not encoded, 0 => not indicated, 3 => reserved */
1074 audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_DOLBY,"surround");
1075 // else if( (parse>>30)==1 )
1076 // audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_DOLBY,"nosurround");
1077 parse<<=2;
1078 }
1079 lfeon=(parse>>31); /* low-frequency effects on */
1080 // calc # channels
1081 switch(acmod) {
1082 case 0: nch=2; break;
1083 case 1: nch=1; break;
1084 case 2: nch=2; break;
1085 case 3: nch=3; break;
1086 case 4: nch=3; break;
1087 case 5: nch=4; break;
1088 case 6: nch=4; break;
1089 case 7: nch=5; break;
1090 }
1091 if( lfeon ) nch++; /* include LFE channel */
1092 sprintf(attr,"%dch",nch);
1093 audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_CHANNELS,attr);
1094 } /*audio_scan_ac3*/
1095
audio_scan_dts(struct audchannel * ach,const unsigned char * buf,int sof,int len)1096 static void audio_scan_dts(struct audchannel *ach,const unsigned char *buf,int sof,int len)
1097 /* gets information about DTS audio. */
1098 {
1099 /* fixme: could determine number of channels and sampling rate, but I'm not bothering for now */
1100 }
1101
audio_scan_pcm(struct audchannel * ach,const unsigned char * buf,int len)1102 static void audio_scan_pcm(struct audchannel *ach,const unsigned char *buf,int len)
1103 /* gets information about LPCM audio. */
1104 {
1105 char attr[6];
1106
1107 switch(buf[1]>>6) {
1108 case 0: audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_QUANT,"16bps"); break;
1109 case 1: audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_QUANT,"20bps"); break;
1110 case 2: audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_QUANT,"24bps"); break;
1111 /* case 3: illegal */
1112 }
1113 sprintf(attr,"%dkhz",48*(1+((buf[1]>>4)&1))); /* 48 or 96kHz */
1114 audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_SAMPLERATE,attr);
1115 sprintf(attr,"%dch",(buf[1]&7)+1); /* nr channels */
1116 audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_CHANNELS,attr);
1117 }
1118
FindVobus(const char * fbase,struct vobgroup * va,vtypes ismenu)1119 int FindVobus(const char *fbase, struct vobgroup *va, vtypes ismenu)
1120 /* collects audio/video/subpicture information, remaps subpicture colours and generates
1121 output VOB files for a menu or titleset, complete except for the NAV packs. */
1122 {
1123 unsigned char *buf;
1124 static unsigned char deferred_buf[2048];
1125 int cursect = 0; /* sector nr in input file */
1126 int fsect = -1; /* sector nr in current output VOB file, -ve => not opened yet */
1127 int vnum;
1128 int outnum = -(int)ismenu + 1; /* +ve for a titleset, in which case used to generate output VOB file names */
1129 int vobid =0;
1130 struct mp2info
1131 {
1132 int hdrptr; /* index at which packet header starts */
1133 unsigned char buf[6]; /* save partial packet in case it crosses sector boundaries */
1134 } mp2hdr[8]; /* enough for the allowed 8 audio streams */
1135 struct colorremap *crs;
1136
1137 crs = malloc(sizeof(struct colorremap) * 32); /* enough for 32 subpicture streams */
1138 for (vnum = 0; vnum < va->numvobs; vnum++)
1139 {
1140 int i, j;
1141 int sysoffs;
1142 bool hadfirstvobu = false;
1143 pts_t backoffs = 0, lastscr = 0;
1144 bool fill_in_vobus = false, got_deferred_buf = false;
1145 struct vob * const thisvob = va->vobs[vnum];
1146 int prevvidsect = -1;
1147 struct vscani vsi;
1148 struct vfile vf;
1149 uint64_t inoffset;
1150 vsi.lastrefsect = 0;
1151 for (i = 0; i < 32; i++)
1152 initremap(crs + i);
1153
1154 vobid++;
1155 thisvob->vobid = vobid;
1156 vsi.firstgop = 1;
1157
1158 fprintf(stderr, "\nSTAT: Processing %s...\n", thisvob->fname);
1159 vf = varied_open(thisvob->fname, O_RDONLY, "input video file");
1160 inoffset = 0;
1161 memset(mp2hdr, 0, 8 * sizeof(struct mp2info));
1162 while (true)
1163 {
1164 if (fsect == 524272)
1165 {
1166 /* VOB file reached maximum allowed size */
1167 writeclose();
1168 if (outnum <= 0)
1169 { /* menu VOB cannot be split */
1170 fprintf(stderr, "\nERR: Menu VOB reached 1gb at inoffset %#"PRIx64"\n", inoffset);
1171 exit(1);
1172 } /*if*/
1173 outnum++; /* for naming next VOB file */
1174 fsect = -1;
1175 } /*if*/
1176 buf = writegrabbuf();
1177 if (got_deferred_buf)
1178 memcpy(buf, deferred_buf, 2048);
1179 else
1180 {
1181 i = fread(buf, 1, 2048, vf.h);
1182 if (i != 2048)
1183 {
1184 if (i == -1)
1185 {
1186 fprintf(stderr, "\nERR: Error %d while reading at inoffset %#"PRIx64": %s\n", errno, inoffset, strerror(errno));
1187 }
1188 else if (i > 0) /* shouldn't occur */
1189 {
1190 fprintf(stderr, "\nERR: Partial sector read (%d bytes at inoffset %#"PRIx64")\n", i, inoffset);
1191 }
1192 else
1193 {
1194 writeundo();
1195 break;
1196 } /*if*/
1197 exit(1);
1198 } /*if*/
1199 } /*if*/
1200 if
1201 (
1202 buf[14] == 0
1203 &&
1204 buf[15] == 0
1205 &&
1206 buf[16] == 1
1207 &&
1208 buf[17] == MPID_PAD
1209 &&
1210 !strcmp((const char *)buf + 20, "dvdauthor-data") /* message from spumux */
1211 )
1212 {
1213 // private dvdauthor data, interpret and remove from final stream
1214 int i = 35;
1215 if (buf[i] != 2)
1216 {
1217 fprintf(stderr, "ERR: dvd info packet at inoffset %#"PRIx64" is unexpected version %d\n", inoffset, buf[i]);
1218 exit(1);
1219 } /*if*/
1220 switch (buf[i + 1]) // packet type
1221 {
1222 case 1: // subtitle/menu color and button information
1223 {
1224 int substreamid = buf[i + 2] & 31;
1225 i += 3;
1226 i += 8; // skip start pts and end pts
1227 while (buf[i] != 0xff)
1228 {
1229 switch (buf[i])
1230 {
1231 case 1: // new colormap
1232 {
1233 int j;
1234 crs[substreamid].origmap = thisvob->progchain->colors;
1235 /* where to merge colours into */
1236 for (j = 0; j < buf[i + 1]; j++)
1237 {
1238 /* collect colours needing remapping, which won't happen
1239 until they're actually referenced */
1240 crs[substreamid].newcolors[j] =
1241 COLOR_UNUSED /* indicate colour needs remapping */
1242 |
1243 buf[i + 2 + 3 * j] << 16
1244 |
1245 buf[i + 3 + 3 * j] << 8
1246 |
1247 buf[i + 4 + 3 * j];
1248 } /*for*/
1249 for (; j < 16; j++) /* fill in unused entries with identity mapping */
1250 crs[substreamid].newcolors[j] = j;
1251 i += 2 + 3 * buf[i + 1];
1252 }
1253 break;
1254 case 2: // new buttoncoli
1255 {
1256 int j;
1257 memcpy(thisvob->buttoncoli, buf + i + 2, buf[i + 1] * 8);
1258 for (j = 0; j < buf[i + 1]; j++)
1259 {
1260 /* remap the colours, not the contrast values */
1261 remapbyte(&crs[substreamid], thisvob->buttoncoli + j * 8 + 0);
1262 remapbyte(&crs[substreamid], thisvob->buttoncoli + j * 8 + 1);
1263 remapbyte(&crs[substreamid], thisvob->buttoncoli + j * 8 + 4);
1264 remapbyte(&crs[substreamid], thisvob->buttoncoli + j * 8 + 5);
1265 } /*for*/
1266 i += 2 + 8 * buf[i + 1];
1267 }
1268 break;
1269 case 3: // button position information
1270 {
1271 int j;
1272 const int nrbuttons = buf[i + 1];
1273 i += 2;
1274 for (j = 0; j < nrbuttons; j++)
1275 {
1276 struct button * b;
1277 struct buttoninfo * bi, bitmp;
1278 char * const bn = readpstr(buf, &i);
1279 if (!findbutton(thisvob->progchain, bn, 0))
1280 {
1281 fprintf
1282 (
1283 stderr,
1284 "ERR: Cannot find button '%s' as referenced by"
1285 " the subtitle at inoffset %#"PRIx64"\n",
1286 bn,
1287 inoffset
1288 );
1289 exit(1);
1290 } /*if*/
1291 b = &thisvob->progchain->buttons[findbutton(thisvob->progchain, bn, 0) - 1];
1292 free(bn);
1293
1294 if (b->numstream >= MAXBUTTONSTREAM)
1295 {
1296 fprintf
1297 (
1298 stderr,
1299 "WARN: Too many button streams at inoffset %#"PRIx64";"
1300 " ignoring buttons\n",
1301 inoffset
1302 );
1303 bi = &bitmp; /* place to put discarded data */
1304 }
1305 else
1306 {
1307 bi = &b->stream[b->numstream++];
1308 } /*if*/
1309 bi->substreamid = substreamid;
1310 i += 2; // skip modifier
1311 bi->autoaction = buf[i++] != 0;
1312 bi->grp = buf[i];
1313 bi->x1 = read2(buf + i + 1);
1314 bi->y1 = read2(buf + i + 3);
1315 bi->x2 = read2(buf + i + 5);
1316 bi->y2 = read2(buf + i + 7);
1317 i += 9;
1318 /* neighbouring button names */
1319 bi->up = readpstr(buf, &i);
1320 bi->down = readpstr(buf, &i);
1321 bi->left = readpstr(buf, &i);
1322 bi->right = readpstr(buf, &i);
1323 } /*for*/
1324 } /*case 3*/
1325 break;
1326 default:
1327 fprintf
1328 (
1329 stderr,
1330 "ERR: dvd info packet command within subtitle at inoffset %#"PRIx64": %d\n",
1331 inoffset,
1332 buf[i]
1333 );
1334 exit(1);
1335 } /*switch*/
1336 } /*while*/
1337
1338 } /*case 1*/
1339 break;
1340
1341 default:
1342 fprintf
1343 (
1344 stderr,
1345 "ERR: unknown dvdauthor-data packet type at inoffset %#"PRIx64": %d\n",
1346 inoffset,
1347 buf[i + 1]
1348 );
1349 exit(1);
1350 } /*switch*/
1351
1352 writeundo(); /* drop private data from output */
1353 continue;
1354 } /*if*/
1355 // we should get a VOBU before a video with GOP
1356 if
1357 (
1358 (fill_in_vobus || !hadfirstvobu)
1359 &&
1360 !got_deferred_buf
1361 &&
1362 has_gop(buf)
1363 )
1364 {
1365 // create VOBU, from Martin Crossley
1366 if (!hadfirstvobu)
1367 { /* let user know the first time this happens */
1368 fprintf
1369 (
1370 stderr,
1371 "INFO: found video GOP at inoffset %#"PRIx64" without a preceding VOBU"
1372 " - creating VOBU\n",
1373 inoffset
1374 );
1375 } /*if*/
1376 fill_in_vobus = true; /* keep doing it from now on */
1377 memcpy(deferred_buf, buf, 2048); /* save just-read sector for processing on next iteration */
1378 got_deferred_buf = true; /* remember I've saved it */
1379 /* buf already has a system header */
1380 buf[41] = MPID_PRIVATE2;
1381 buf[42] = 0x03;
1382 buf[43] = 0xd4;
1383 buf[44] = 0x81; /* rest of PCI will be correctly filled in later by FixVobus */
1384 memset(buf + 45, 0, 2048 - 45);
1385 buf[1026] = 1;
1386 buf[1027] = MPID_PRIVATE2;
1387 buf[1028] = 0x03;
1388 buf[1029] = 0xfa;
1389 buf[1030] = 0x81; /* rest of DSI will be correctly filled in later by FixVobus */
1390 }
1391 else if (got_deferred_buf)
1392 got_deferred_buf = false; /* already picked it up */
1393 if (buf[0] == 0 && buf[1] == 0 && buf[2] == 1 && buf[3] == MPID_PACK)
1394 {
1395 const pts_t newscr = readscr(buf + 4);
1396 if (hadfirstvobu && newscr == 0 && lastscr > 0)
1397 /* suggestion from Philippe Sarazin -- alternatively, Shaun Jackman suggests
1398 simply treating newscr < lastscr as a warning and continuing */
1399 {
1400 backoffs -= lastscr; /* adjust to remove SCR discontinuity */
1401 fprintf(stderr, "\nWARN: SCR reset at inoffset %#"PRIx64". New back offset = %" PRId64"\n", inoffset, backoffs);
1402 }
1403 else if (newscr < lastscr)
1404 {
1405 fprintf
1406 (
1407 stderr,
1408 "ERR: SCR moves backwards at inoffset %#"PRIx64","
1409 " remultiplex input: %" PRId64" < %" PRId64"\n",
1410 inoffset,
1411 newscr,
1412 lastscr
1413 );
1414 exit(1);
1415 } /*if*/
1416 lastscr = newscr;
1417 if (!hadfirstvobu)
1418 backoffs = newscr; /* start SCR from 0 */
1419 } /*if*/
1420 transpose_ts(buf, -backoffs);
1421 if (fsect == -1)
1422 {
1423 /* start a new VOB file */
1424 fsect = 0;
1425 if (fbase)
1426 {
1427 char * newname;
1428 if (outnum >= 0)
1429 {
1430 newname = sprintf_alloc("%s_%d.VOB", fbase, outnum);
1431 }
1432 else
1433 {
1434 newname = strdup(fbase);
1435 } /*if*/
1436 writeopen(newname);
1437 free(newname);
1438 } /*if*/
1439 } /*if*/
1440 if
1441 (
1442 buf[14] == 0
1443 &&
1444 buf[15] == 0
1445 &&
1446 buf[16] == 1
1447 &&
1448 buf[17] == MPID_SYSTEM
1449 )
1450 {
1451 if
1452 (
1453 buf[38] == 0
1454 &&
1455 buf[39] == 0
1456 &&
1457 buf[40] == 1
1458 &&
1459 buf[41] == MPID_PRIVATE2 // 1st private2
1460 &&
1461 buf[1024] == 0
1462 &&
1463 buf[1025] == 0
1464 &&
1465 buf[1026] == 1
1466 &&
1467 buf[1027] == MPID_PRIVATE2 // 2nd private2
1468 ) /* looks like a NAV PACK, which means the start of a new VOBU */
1469 {
1470 struct vobuinfo *vi;
1471 if (thisvob->numvobus)
1472 finishvideoscan(va, vnum, prevvidsect, &vsi);
1473 // fprintf(stderr, "INFO: vobu at inoffset %#"PRIx64"\n", inoffset);
1474 hadfirstvobu = true; /* NAV PACK starts a VOBU */
1475 if (thisvob->numvobus == thisvob->maxvobus) /* need more space */
1476 {
1477 if (!thisvob->maxvobus)
1478 thisvob->maxvobus = 1; /* first allocation */
1479 else
1480 thisvob->maxvobus <<= 1;
1481 /* resize in powers of 2 to reduce reallocation calls */
1482 thisvob->vobu = (struct vobuinfo *)realloc
1483 (
1484 /*ptr =*/ thisvob->vobu,
1485 /*size =*/ thisvob->maxvobus * sizeof(struct vobuinfo)
1486 );
1487 } /*if*/
1488 vi = &thisvob->vobu[thisvob->numvobus]; /* for the new VOBU */
1489 memset(vi, 0, sizeof(struct vobuinfo));
1490 vi->sector = cursect;
1491 vi->fsect = fsect;
1492 vi->fnum = outnum;
1493 vi->firstvideopts = -1;
1494 vi->firstIfield = 0;
1495 vi->numfields = 0;
1496 vi->numref = 0;
1497 vi->hasseqend = 0;
1498 vi->hasvideo = 0;
1499 memcpy(thisvob->vobu[thisvob->numvobus].sectdata, buf, 0x26); // save pack and system header; the rest will be reconstructed later
1500 thisvob->numvobus++;
1501 if (!(thisvob->numvobus & 15)) /* time to let user know progress */
1502 printvobustatus(va, cursect, false);
1503 vsi.lastrefsect = 0;
1504 vsi.firstgop = 1; /* restart scan for first GOP */
1505 } /*if*/
1506 } /*if*/
1507 if (!hadfirstvobu)
1508 {
1509 fprintf
1510 (
1511 stderr,
1512 "WARN: Skipping sector at inoffset %#"PRIx64", waiting for first VOBU...\n",
1513 inoffset
1514 );
1515 writeundo(); /* ignore it */
1516 continue;
1517 } /*if*/
1518 thisvob->vobu[thisvob->numvobus - 1].lastsector = cursect;
1519
1520 i = 14;
1521 j = -1;
1522 while (i <= 2044)
1523 {
1524 if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1)
1525 {
1526 if (buf[i + 3] >= MPID_PRIVATE1 && buf[i + 3] <= MPID_VIDEO_LAST)
1527 /* private, padding, audio or video stream */
1528 {
1529 j = i;
1530 i += 6 + read2(buf + i + 4); /* start of next packet */
1531 continue;
1532 }
1533 else if
1534 (
1535 buf[i + 3] == MPID_PROGRAM_END
1536 &&
1537 j >= 14
1538 &&
1539 buf[j + 3] == MPID_PAD /* previous was padding stream */
1540 )
1541 {
1542 write2(buf + j + 4, read2(buf + j + 4) + 4);
1543 /* merge program-end packet into prior pad packet */
1544 memset(buf + i, 0, 4); // mplex uses 0 for padding, so will I
1545 } /*if*/
1546 } /*if*/
1547 break;
1548 } /*while*/
1549
1550 sysoffs =
1551 buf[14] == 0 && buf[15] == 0 && buf[16] == 1 && buf[17] == MPID_SYSTEM ?
1552 /* skip system header if present */
1553 (buf[18] << 8 | buf[19]) + 6
1554 :
1555 0;
1556 if
1557 (
1558 buf[0] == 0
1559 &&
1560 buf[1] == 0
1561 &&
1562 buf[2] == 1
1563 &&
1564 buf[3] == MPID_PACK
1565 &&
1566 buf[14 + sysoffs] == 0
1567 &&
1568 buf[15 + sysoffs] == 0
1569 &&
1570 buf[16 + sysoffs] == 1
1571 &&
1572 buf[17 + sysoffs] == MPID_VIDEO_FIRST /* only video stream */
1573 )
1574 {
1575 struct vobuinfo * const vi = &thisvob->vobu[thisvob->numvobus - 1];
1576 vi->hasvideo = 1;
1577 scanvideoframe(va, buf + sysoffs, vi, cursect, prevvidsect, &vsi);
1578 if
1579 (
1580 (buf[21 + sysoffs] & 128) /* PTS present */
1581 &&
1582 vi->firstvideopts == -1 /* not seen one yet */
1583 )
1584 {
1585 vi->firstvideopts = readpts(buf + 23 + sysoffs);
1586 } /*if*/
1587 prevvidsect = cursect;
1588 } /*if*/
1589 if
1590 (
1591 buf[0] == 0
1592 &&
1593 buf[1] == 0
1594 &&
1595 buf[2] == 1
1596 &&
1597 buf[3] == MPID_PACK
1598 &&
1599 buf[14 + sysoffs] == 0
1600 &&
1601 buf[15 + sysoffs] == 0
1602 &&
1603 buf[16 + sysoffs] == 1
1604 &&
1605 (
1606 (buf[17 + sysoffs] & 0xf8) == 0xc0 /* MPEG audio stream */
1607 ||
1608 buf[17 + sysoffs] == MPID_PRIVATE1 /* DVD audio or subpicture */
1609 )
1610 )
1611 {
1612 pts_t pts0 = 0, pts1 = 0, backpts1 = 0;
1613 const int dptr = buf[22 + sysoffs] /* PES header data length */ + 23 + sysoffs; /* offset to packet data */
1614 const int endop = read2(buf + 18 + sysoffs) /* PES packet length */ + 20 /* fixed PES header length */ + sysoffs; /* end of packet */
1615 int audch;
1616 const int haspts = (buf[21 + sysoffs] & 128) != 0;
1617 if (buf[17 + sysoffs] == MPID_PRIVATE1) /* DVD audio or subpicture */
1618 {
1619 const int sid = buf[dptr]; /* sub-stream ID */
1620 const int offs = read2(buf + dptr + 2);
1621 /* offset to audio sample frame which corresponds to PTS value */
1622 const int nrframes = buf[dptr + 1];
1623 /* nr audio sample frames beginning in this packet */
1624 switch (sid & 0xf8)
1625 {
1626 case 0x20: // subpicture
1627 case 0x28: // subpicture
1628 case 0x30: // subpicture
1629 case 0x38: // subpicture
1630 audch = sid;
1631 break;
1632 case 0x80: // ac3 audio
1633 pts1 += 2880 * nrframes;
1634 audch = sid & 7;
1635 audio_scan_ac3(&thisvob->audch[audch], buf + dptr + 4, offs - 1, endop - (dptr + 4));
1636 break;
1637 case 0x88: // dts audio
1638 /* pts1 += 960 * nrframes; */ /* why not? */
1639 audch = 24 | (sid & 7);
1640 audio_scan_dts(&thisvob->audch[audch], buf + dptr + 4, offs - 1, endop - (dptr + 4));
1641 break;
1642 case 0xa0: // pcm audio
1643 pts1 += 150 * nrframes;
1644 audch = 16 | (sid & 7);
1645 audio_scan_pcm(&thisvob->audch[audch], buf + dptr + 4, endop - (dptr + 4));
1646 break;
1647 default: // unknown
1648 audch = -1;
1649 break;
1650 } /*switch*/
1651 }
1652 else /* regular MPEG audio */
1653 {
1654 const int len = endop - dptr; /* length of packet data */
1655 const int index = buf[17 + sysoffs] & 7; /* audio stream ID */
1656 audch = 8 | index; // mp2
1657 memcpy(mp2hdr[index].buf + 3, buf + dptr, 3);
1658 while (mp2hdr[index].hdrptr + 4 <= len)
1659 {
1660 const unsigned char * h;
1661 if (mp2hdr[index].hdrptr < 0)
1662 h = mp2hdr[index].buf + 3 + mp2hdr[index].hdrptr;
1663 /* overlap from previous */
1664 else
1665 h = buf + dptr + mp2hdr[index].hdrptr;
1666 if (!mpa_valid(h))
1667 {
1668 mp2hdr[index].hdrptr++; /* try the next likely offset */
1669 continue;
1670 } /*if*/
1671 if (mp2hdr[index].hdrptr < 0)
1672 backpts1 += 2160; /* how much time to add to end of previous packet */
1673 else
1674 pts1 += 2160;
1675 mp2hdr[index].hdrptr += mpa_len(h); /* to next header */
1676 } /*while*/
1677 mp2hdr[index].hdrptr -= len; /* will be -ve if extends into next sector */
1678 memcpy(mp2hdr[index].buf, buf + dptr + len - 3, 3);
1679 audiodesc_set_audio_attr(&thisvob->audch[audch].ad, &thisvob->audch[audch]. adwarn, AUDIO_SAMPLERATE, "48khz");
1680 } /*if*/
1681 /* at this point, pts1 is the duration of the audio in the packet (0 for subpicture) */
1682 if (haspts)
1683 {
1684 pts0 = readpts(buf + 23 + sysoffs);
1685 pts1 += pts0;
1686 }
1687 else if (pts1 > 0)
1688 {
1689 fprintf
1690 (
1691 stderr,
1692 "WARN: Audio channel %d contains sync headers at inoffset %#"PRIx64" but has no PTS.\n",
1693 audch,
1694 inoffset
1695 );
1696 } /*if*/
1697 // fprintf(stderr,"aud ch=%d pts %d - %d (%d)\n",audch,pts0,pts1,pts1-pts0);
1698 // fprintf(stderr,"pts[%d] %d (%02x %02x %02x %02x %02x)\n",va->numaudpts,pts,buf[23],buf[24],buf[25],buf[26],buf[27]);
1699 if (audch < 0 || audch >= 64)
1700 {
1701 fprintf(stderr,"WARN: Invalid audio channel %d at inoffset %#"PRIx64"\n", audch, inoffset);
1702 /* and ignore */
1703 }
1704 else if (haspts)
1705 {
1706 struct audchannel * const ach = &thisvob->audch[audch];
1707 if (ach->numaudpts == ach->maxaudpts) { /* need more space */
1708 if (ach->maxaudpts)
1709 ach->maxaudpts <<= 1;
1710 /* resize in powers of 2 to reduce reallocation calls */
1711 else
1712 ach->maxaudpts = 1; /* first allocation */
1713 ach->audpts = (struct audpts *)realloc
1714 (
1715 /*ptr =*/ ach->audpts,
1716 /*size =*/ ach->maxaudpts * sizeof(struct audpts)
1717 );
1718 } /*if*/
1719 if (ach->numaudpts)
1720 {
1721 // we cannot compute the length of a DTS audio packet
1722 // so just backfill if it is one
1723 // otherwise, for mp2 add any pts to the previous
1724 // sector for a header that spanned two sectors
1725 if ((audch & 0x38) == 0x18) // is this DTS?
1726 ach->audpts[ach->numaudpts - 1].pts[1] = pts0;
1727 else
1728 ach->audpts[ach->numaudpts - 1].pts[1] += backpts1;
1729
1730 if (ach->audpts[ach->numaudpts - 1].pts[1] < pts0)
1731 {
1732 if (audch >= 32)
1733 goto noshow; /* not audio */
1734 fprintf
1735 (
1736 stderr,
1737 "WARN: Discontinuity of %" PRId64" in audio channel %d"
1738 " at inoffset %#"PRIx64"; please remultiplex input.\n",
1739 pts0 - ach->audpts[ach->numaudpts - 1].pts[1],
1740 audch,
1741 inoffset
1742 );
1743 // fprintf(stderr,"last=%d, this=%d\n",ach->audpts[ach->numaudpts-1].pts[1],pts0);
1744 }
1745 else if (ach->audpts[ach->numaudpts - 1].pts[1] > pts0)
1746 fprintf
1747 (
1748 stderr,
1749 "WARN: %s pts for channel %d moves backwards by %"
1750 PRId64 " at inoffset %#"PRIx64"; please remultiplex input.\n",
1751 audch >= 32 ? "Subpicture" : "Audio",
1752 audch,
1753 ach->audpts[ach->numaudpts - 1].pts[1] - pts0,
1754 inoffset
1755 );
1756 else
1757 goto noshow;
1758 fprintf(stderr, "WARN: Previous sector: ");
1759 printpts(ach->audpts[ach->numaudpts - 1].pts[0]);
1760 fprintf(stderr, " - ");
1761 printpts(ach->audpts[ach->numaudpts - 1].pts[1]);
1762 fprintf(stderr, "\nWARN: Current sector: ");
1763 printpts(pts0);
1764 fprintf(stderr, " - ");
1765 printpts(pts1);
1766 fprintf(stderr, "\n");
1767 ach->audpts[ach->numaudpts - 1].pts[1] = pts0;
1768 } /*if*/
1769 noshow:
1770 /* fill in new entry */
1771 ach->audpts[ach->numaudpts].pts[0] = pts0;
1772 ach->audpts[ach->numaudpts].pts[1] = pts1;
1773 ach->audpts[ach->numaudpts].asect = cursect;
1774 ach->numaudpts++;
1775 } /*if*/
1776 } /*if*/
1777 // the following code scans subtitle code in order to
1778 // remap the colors and update the end pts
1779 if
1780 (
1781 buf[0] == 0
1782 &&
1783 buf[1] == 0
1784 &&
1785 buf[2] == 1
1786 &&
1787 buf[3] == MPID_PACK
1788 &&
1789 buf[14 + sysoffs] == 0
1790 &&
1791 buf[15 + sysoffs] == 0
1792 &&
1793 buf[16 + sysoffs] == 1
1794 &&
1795 buf[17 + sysoffs] == MPID_PRIVATE1
1796 )
1797 {
1798 int dptr = buf[22 + sysoffs] /* PES header data length */ + 23 + sysoffs; /* offset to packet data */
1799 const int ml = read2(buf + 18) /* PES packet length */ + 20 /* fixed PES header length */ + sysoffs; /* end of packet */
1800 const int st = buf[dptr]; /* sub-stream ID */
1801 dptr++; /* skip sub-stream ID */
1802 if ((st & 0xe0) == 0x20)
1803 { /* subpicture stream */
1804 procremap
1805 (
1806 /*cr =*/ &crs[st & 31],
1807 /*b =*/ buf + dptr,
1808 /*blen =*/ ml - dptr,
1809 /*timespan =*/
1810 &thisvob->audch[st].audpts[thisvob->audch[st].numaudpts - 1].pts[1]
1811 );
1812 } /*if*/
1813 } /*if*/
1814 cursect++;
1815 fsect++;
1816 inoffset += 2048;
1817 } /*while*/
1818 varied_close(vf);
1819 if (thisvob->numvobus)
1820 {
1821 int i;
1822 pts_t finalaudiopts;
1823 finishvideoscan(va, vnum, prevvidsect, &vsi);
1824 // find end of audio
1825 finalaudiopts = -1;
1826 for (i = 0; i < 32; i++)
1827 {
1828 struct audchannel * const ach = thisvob->audch + i;
1829 if
1830 (
1831 ach->numaudpts
1832 &&
1833 ach->audpts[ach->numaudpts - 1].pts[1] > finalaudiopts
1834 )
1835 finalaudiopts = ach->audpts[ach->numaudpts - 1].pts[1];
1836 } /*for*/
1837 // pin down all video vobus
1838 // note: we make two passes; one assumes that the PTS for the
1839 // first frame is exact; the other assumes that the PTS for
1840 // the first frame is off by 1/2. If both fail, then the third pass
1841 // assumes things are exact and throws a warning
1842 for (i = 0; i < 3; i++)
1843 {
1844 pts_t pts_align = -1; /* initially undefined */
1845 int complained = 0, j;
1846 for (j = 0; j < thisvob->numvobus; j++)
1847 {
1848 struct vobuinfo * const vi = thisvob->vobu + j;
1849 if (vi->hasvideo)
1850 {
1851 if (pts_align == -1)
1852 {
1853 pts_align = vi->firstvideopts * 2;
1854 if (i == 1)
1855 {
1856 // I assume pts should round down? That seems to be how mplex deals with it
1857 // also see earlier comment
1858
1859 // since pts round down, then the alternative base we should try is
1860 // firstvideopts+0.5, thus increment
1861 pts_align++;
1862 } /*if*/
1863 // MarkChapters will complain if firstIfield!=0
1864 } /*if*/
1865
1866 vi->videopts[0] = calcpts(va, i == 2, &complained, &pts_align, vi->firstvideopts, -vi->firstIfield);
1867 vi->videopts[1] = calcpts(va, i == 2, &complained, &pts_align, vi->firstvideopts, -vi->firstIfield + vi->numfields);
1868 // if this looks like a dud, abort and try the next pass
1869 if (complained && i < 2)
1870 break;
1871 vi->sectpts[0] = vi->videopts[0];
1872 if (j + 1 == thisvob->numvobus && finalaudiopts > vi->videopts[1])
1873 vi->sectpts[1] = finalaudiopts;
1874 else
1875 vi->sectpts[1] = vi->videopts[1];
1876 } /*if*/
1877 } /*for*/
1878 if (!complained)
1879 break;
1880 } /*for*/
1881 // guess at non-video vobus
1882 for (i = 0; i < thisvob->numvobus; i++)
1883 {
1884 struct vobuinfo * const vi = thisvob->vobu + i;
1885 if (!vi->hasvideo)
1886 {
1887 int j, k;
1888 pts_t firstaudiopts = -1, p;
1889
1890 for (j = 0; j < 32; j++)
1891 {
1892 const struct audchannel * const ach = thisvob->audch + j;
1893 for (k = 0; k < ach->numaudpts; k++)
1894 if (ach->audpts[k].asect >= vi->sector)
1895 {
1896 if (firstaudiopts == -1 || ach->audpts[k].pts[0] < firstaudiopts)
1897 firstaudiopts = ach->audpts[k].pts[0];
1898 break;
1899 } /*if; for*/
1900 } /*for*/
1901 if (firstaudiopts == -1)
1902 {
1903 fprintf
1904 (
1905 stderr,
1906 "WARN: Cannot detect pts for VOBU at inoffset %#"PRIx64" if there is"
1907 " no audio or video\nWARN: Using SCR instead.\n",
1908 inoffset
1909 );
1910 firstaudiopts = readscr(vi->sectdata + 4) + 4 * 147;
1911 // 147 is roughly the minimum pts that must transpire between packets;
1912 // we give a couple packets of buffer to allow the dvd player to
1913 // process the data
1914 } /*if*/
1915 if (i)
1916 {
1917 pts_t frpts = getframepts(va);
1918 p = firstaudiopts - thisvob->vobu[i - 1].sectpts[0];
1919 // ensure this is a multiple of a framerate, just to be nice
1920 p += frpts - 1;
1921 p -= p % frpts;
1922 p += thisvob->vobu[i - 1].sectpts[0];
1923 if (p < thisvob->vobu[i - 1].sectpts[1])
1924 {
1925 fprintf
1926 (
1927 stderr,
1928 "ERR: pts %"PRId64" rounded up to %"PRId64" at rate"
1929 " %"PRId64" lies within previous vobu %d"
1930 " [%"PRId64"..%"PRId64"] at inoffset %#"PRIx64"\n",
1931 firstaudiopts,
1932 p,
1933 frpts,
1934 i - 1,
1935 thisvob->vobu[i - 1].sectpts[0],
1936 thisvob->vobu[i - 1].sectpts[1],
1937 inoffset
1938 );
1939 exit(1);
1940 } /*if*/
1941 thisvob->vobu[i - 1].sectpts[1] = p;
1942 }
1943 else
1944 {
1945 fprintf
1946 (
1947 stderr,
1948 "ERR: Cannot infer pts for VOBU at inoffset %#"PRIx64" if there is"
1949 " no audio or video and it is the\nERR: first VOBU.\n",
1950 inoffset
1951 );
1952 exit(1);
1953 } /*if*/
1954 vi->sectpts[0] = p;
1955 // if we can easily predict the end pts of this sector,
1956 // then fill it in. otherwise, let the next iteration do it
1957 if (i + 1 == thisvob->numvobus)
1958 { // if this is the end of the vob, use the final audio pts as the last pts
1959 if( finalaudiopts>vi->sectpts[0] )
1960 p = finalaudiopts;
1961 else
1962 p = vi->sectpts[0] + getframepts(va);
1963 // add one frame of a buffer, so we don't have a zero (or less) length vobu
1964 }
1965 else if (thisvob->vobu[i+1].hasvideo)
1966 // if the next vobu has video, use the start of the video as the end of this vobu
1967 p = thisvob->vobu[i + 1].sectpts[0];
1968 else
1969 // the next vobu is an audio only vobu, and will backfill the pts as necessary
1970 continue;
1971 if (p <= vi->sectpts[0])
1972 {
1973 fprintf
1974 (
1975 stderr,
1976 "ERR: Audio and video are too poorly synchronised at inoffset"
1977 " %#"PRIx64"; you must remultiplex.\n",
1978 inoffset
1979 );
1980 exit(1);
1981 } /*if*/
1982 vi->sectpts[1] = p;
1983 } /*if*/
1984 } /*for*/
1985
1986 fprintf(stderr, "\nINFO: Video pts = ");
1987 printpts(thisvob->vobu[0].videopts[0]);
1988 fprintf(stderr, " .. ");
1989 for (i = thisvob->numvobus - 1; i >= 0; i--)
1990 if (thisvob->vobu[i].hasvideo)
1991 {
1992 printpts(thisvob->vobu[i].videopts[1]);
1993 break;
1994 } /*if; for*/
1995 if (i < 0)
1996 fprintf(stderr, "??");
1997 for (i = 0; i < 64; i++)
1998 {
1999 const struct audchannel * const ach = &thisvob->audch[i];
2000 if (ach->numaudpts)
2001 {
2002 fprintf(stderr, "\nINFO: Audio[%d] pts = ", i);
2003 printpts(ach->audpts[0].pts[0]);
2004 fprintf(stderr, " .. ");
2005 printpts(ach->audpts[ach->numaudpts - 1].pts[1]);
2006 } /*if*/
2007 } /*for*/
2008 fprintf(stderr, "\n");
2009 } /*if*/
2010 } /*for*/
2011 writeclose();
2012 printvobustatus(va, cursect, true);
2013 fprintf(stderr, "\n");
2014 free(crs);
2015 return 1;
2016 } /*FindVobus*/
2017
pabs(pts_t pts)2018 static pts_t pabs(pts_t pts)
2019 /* returns the absolute value of pts. */
2020 {
2021 if (pts < 0)
2022 return -pts;
2023 return pts;
2024 } /*pabs*/
2025
findnearestvobu(struct vob * va,pts_t pts)2026 static int findnearestvobu(struct vob *va, pts_t pts)
2027 /* returns the index of the VOBU closest in time to pts. */
2028 {
2029 int l = 0, h = va->numvobus - 1, i;
2030 if (h < 0)
2031 return -1;
2032 pts += va->vobu[0].sectpts[0];
2033 i = findvobu(va, pts, l, h);
2034 if
2035 (
2036 i + 1 < va->numvobus
2037 &&
2038 i >= 0
2039 &&
2040 pabs(pts - va->vobu[i + 1].sectpts[0]) < pabs(pts - va->vobu[i].sectpts[0])
2041 /* next one is closer */
2042 )
2043 i++;
2044 return i;
2045 } /*findnearestvobu*/
2046
MarkChapters(struct vobgroup * va)2047 void MarkChapters(struct vobgroup *va)
2048 /* fills in scellid, ecellid, vobcellid, firstvobuincell, lastvobuincell, numcells fields
2049 to mark all the cells and programs. */
2050 {
2051 int i, j, k, lastvobuid;
2052 // mark start and stop points
2053 lastvobuid = -1;
2054 for (i = 0; i < va->numallpgcs; i++)
2055 for (j = 0; j < va->allpgcs[i]->numsources; j++)
2056 {
2057 /* use vobcellid fields to mark start of cells, and scellid and ecellid fields
2058 to hold vobu indexes, all to be replaced later with right values */
2059 struct source * const thissource = va->allpgcs[i]->sources[j];
2060 for (k = 0; k < thissource->numcells; k++)
2061 {
2062 int v;
2063 v = findnearestvobu(thissource->vob, thissource->cells[k].startpts);
2064 if (v >= 0 && v < thissource->vob->numvobus)
2065 {
2066 if (thissource->cells[k].ischapter != CELL_NEITHER) /* from Wolfgang Wershofen */
2067 { /* info for user corresponding to the points they marked */
2068 fprintf(stderr, "CHAPTERS: VTS[%d/%d] ", i + 1, j + 1);
2069 printpts(thissource->vob->vobu[v].sectpts[0] - thissource->vob->vobu[0].sectpts[0]);
2070 fprintf(stderr, "\n");
2071 } /*if*/
2072 thissource->vob->vobu[v].vobcellid = 1; /* cell starts here */
2073 } /*if*/
2074 thissource->cells[k].scellid = v; /* cell starts here */
2075 if
2076 (
2077 lastvobuid != v
2078 &&
2079 thissource->vob->vobu[v].firstIfield != 0
2080 )
2081 {
2082 fprintf
2083 (
2084 stderr,
2085 "WARN: GOP may not be closed on cell %d of source %s of pgc %d\n",
2086 k + 1,
2087 thissource->fname,
2088 i + 1
2089 );
2090 } /*if*/
2091 if (thissource->cells[k].endpts >= 0)
2092 {
2093 v = findnearestvobu(thissource->vob, thissource->cells[k].endpts);
2094 if (v >= 0 && v < thissource->vob->numvobus)
2095 thissource->vob->vobu[v].vobcellid = 1; /* another cell should start here */
2096 }
2097 else /* -ve end time => cell goes to end of vob */
2098 v = thissource->vob->numvobus;
2099 thissource->cells[k].ecellid = v; /* next cell starts here */
2100 lastvobuid = v;
2101 } /*for*/
2102 } /*for; for*/
2103 /* At this point, the vobcellid fields have been set to 1 for all VOBUs which
2104 are supposed to start new cells. */
2105 for (i = 0; i < va->numvobs; i++)
2106 {
2107 /* fill in the vobcellid fields with the right values, and also
2108 firstvobuincell and lastvobuincell */
2109 int cellvobu = 0;
2110 int cellid = 0;
2111 va->vobs[i]->vobu[0].vobcellid = 1; /* ensure first vobu starts a cell */
2112 for (j = 0; j < va->vobs[i]->numvobus; j++)
2113 {
2114 struct vobuinfo * const thisvobu = &va->vobs[i]->vobu[j];
2115 if (thisvobu->vobcellid)
2116 {
2117 cellid++; /* start new cell */
2118 cellvobu = j; /* this VOBU is first in cell */
2119 } /*if*/
2120 thisvobu->vobcellid = cellid + va->vobs[i]->vobid * 256;
2121 thisvobu->firstvobuincell = cellvobu;
2122 } /*for*/
2123 cellvobu = va->vobs[i]->numvobus - 1;
2124 for (j = cellvobu; j >= 0; j--)
2125 { /* fill in the lastvobuincell fields */
2126 struct vobuinfo * const thisvobu = &va->vobs[i]->vobu[j];
2127 thisvobu->lastvobuincell = cellvobu;
2128 if (thisvobu->firstvobuincell == j) /* reached start of this cell */
2129 cellvobu = j - 1;
2130 } /*for*/
2131 va->vobs[i]->numcells = cellid;
2132 if (cellid >= 256)
2133 {
2134 fprintf
2135 (
2136 stderr,
2137 "ERR: VOB %s has too many cells (%d, 256 allowed)\n",
2138 va->vobs[i]->fname,
2139 cellid
2140 );
2141 exit(1);
2142 } /*if*/
2143 } /*for*/
2144 /* Now fill in right values for scellid and ecellid fields, replacing the
2145 vobu indexes I previously put in with the corresponding vobcellid values I
2146 have just computed. */
2147 for (i = 0; i < va->numallpgcs; i++)
2148 for (j = 0; j < va->allpgcs[i]->numsources; j++)
2149 {
2150 struct source * const thissource = va->allpgcs[i]->sources[j];
2151 for (k = 0; k < thissource->numcells; k++)
2152 {
2153 struct cell * const thiscell = &thissource->cells[k];
2154 if (thiscell->scellid < 0)
2155 thiscell->scellid = 1;
2156 else if (thiscell->scellid < thissource->vob->numvobus)
2157 thiscell->scellid = thissource->vob->vobu[thiscell->scellid].vobcellid & 255;
2158 else
2159 thiscell->scellid = thissource->vob->numcells + 1;
2160 if (thiscell->ecellid < 0)
2161 thiscell->ecellid = 1;
2162 else if (thiscell->ecellid < thissource->vob->numvobus)
2163 thiscell->ecellid = thissource->vob->vobu[thiscell->ecellid].vobcellid & 255;
2164 else
2165 thiscell->ecellid = thissource->vob->numcells + 1;
2166 /* near as I can tell, ecellid will either be equal to scellid or 1 greater */
2167 va->allpgcs[i]->numcells += thiscell->ecellid - thiscell->scellid;
2168 if (thiscell->scellid != thiscell->ecellid && thiscell->ischapter != CELL_NEITHER)
2169 {
2170 va->allpgcs[i]->numprograms++; /* ischapter is CELL_PROGRAM or CELL_CHAPTER_PROGRAM */
2171 if (thiscell->ischapter == CELL_CHAPTER_PROGRAM)
2172 va->allpgcs[i]->numchapters++;
2173 if (va->allpgcs[i]->numprograms >= 256)
2174 {
2175 fprintf
2176 (
2177 stderr,
2178 "ERR: PGC %d has too many programs (%d, 256 allowed)\n",
2179 i + 1,
2180 va->allpgcs[i]->numprograms
2181 );
2182 exit(1);
2183 } /*if*/
2184 // if numprograms<256, then numchapters<256, so
2185 // no need to doublecheck
2186 } /*if*/
2187 } /*for*/
2188 } /*for; for*/
2189 } /*MarkChapters*/
2190
getcellaudiopts(const struct vobgroup * va,int vcid,int ach,int w)2191 static pts_t getcellaudiopts(const struct vobgroup *va,int vcid,int ach,int w)
2192 {
2193 const struct vob *v=va->vobs[(vcid>>8)-1];
2194 const struct audchannel *a=&v->audch[ach];
2195 int ai=0;
2196
2197 assert((vcid&255)==(w?v->numcells:1));
2198 if( w )
2199 ai=a->numaudpts-1;
2200 return a->audpts[ai].pts[w];
2201 }
2202
hasaudio(const struct vobgroup * va,int vcid,int ach,int w)2203 static int hasaudio(const struct vobgroup *va,int vcid,int ach,int w)
2204 {
2205 const struct vob *v=va->vobs[(vcid>>8)-1];
2206 const struct audchannel *a=&v->audch[ach];
2207
2208 assert((vcid&255)==(w?v->numcells:1));
2209
2210 return a->numaudpts!=0;
2211 }
2212
getcellvideopts(const struct vobgroup * va,int vcid,int w)2213 static pts_t getcellvideopts(const struct vobgroup *va,int vcid,int w)
2214 {
2215 const struct vob *v=va->vobs[(vcid>>8)-1];
2216 int vi=0;
2217
2218 assert((vcid&255)==(w?v->numcells:1));
2219 if( w )
2220 vi=v->numvobus-1;
2221 // we use sectpts instead of videopts because sometimes you will
2222 // present the last video frame for a long time; we want to know
2223 // the last presented time stamp: sectpts
2224 return v->vobu[vi].sectpts[w];
2225 }
2226
calcaudiodiff(const struct vobgroup * va,int vcid,int ach,int w)2227 static pts_t calcaudiodiff(const struct vobgroup *va,int vcid,int ach,int w)
2228 {
2229 return getcellvideopts(va,vcid,w)-getcellaudiopts(va,vcid,ach,w);
2230 }
2231
calcaudiogap(const struct vobgroup * va,int vcid0,int vcid1,int ach)2232 int calcaudiogap(const struct vobgroup *va,int vcid0,int vcid1,int ach)
2233 {
2234 if( vcid0==-1 || vcid1==-1 )
2235 return 0;
2236 if( vcid1==vcid0+1 )
2237 return 0;
2238 if( (vcid1&255)==1 && va->vobs[(vcid0>>8)-1]->numcells==(vcid0&255) ) {
2239 int g1,g2;
2240
2241 // there is no discontinuity if there is no audio in the second half
2242 if( !hasaudio(va,vcid1,ach,0) )
2243 return 0;
2244
2245 // we have problems if the second half has audio but the first doesn't
2246 if( !hasaudio(va,vcid0,ach,1) && hasaudio(va,vcid1,ach,0) ) {
2247 fprintf(stderr,"WARN: Transition from non-audio to audio VOB; assuming discontinuity.\n");
2248 return 1;
2249 }
2250
2251 g1=calcaudiodiff(va,vcid0,ach,1);
2252 g2=calcaudiodiff(va,vcid1,ach,0);
2253 return g1!=g2;
2254 }
2255 fprintf(stderr,"WARN: Do not know how to compute the audio gap between '%s' and '%s', assuming discontinuity.\n",va->vobs[(vcid0>>8)-1]->fname,va->vobs[(vcid1>>8)-1]->fname);
2256 return 1;
2257 }
2258
FixVobus(const char * fbase,const struct vobgroup * va,const struct workset * ws,vtypes ismenu)2259 void FixVobus(const char *fbase,const struct vobgroup *va,const struct workset *ws,vtypes ismenu)
2260 /* fills in the NAV packs (i.e. PCI and DSI packets) for each VOBU in the
2261 already-written output VOB files. */
2262 {
2263 int outvob = -1;
2264 int vobuindex, j, pn, fnum = -2;
2265 pts_t scr;
2266 int vff, vrew;
2267 int totvob, curvob; /* for displaying statistics */
2268
2269 totvob = 0;
2270 for (pn = 0; pn < va->numvobs; pn++)
2271 totvob += va->vobs[pn]->numvobus;
2272 curvob = 0;
2273
2274 for (pn = 0; pn < va->numvobs; pn++)
2275 {
2276 const struct vob * const thisvob = va->vobs[pn];
2277 for (vobuindex = 0; vobuindex < thisvob->numvobus; vobuindex++)
2278 {
2279 const struct vobuinfo * const thisvobu = &thisvob->vobu[vobuindex];
2280 static unsigned char buf[2048];
2281
2282 if (thisvobu->fnum != fnum)
2283 {
2284 /* time to start a new output file */
2285 if (outvob >= 0)
2286 flushclose(outvob);
2287 fnum = thisvobu->fnum;
2288 if (fbase)
2289 {
2290 char * fname;
2291 if (fnum == -1)
2292 {
2293 fname = strdup(fbase);
2294 }
2295 else
2296 {
2297 fname = sprintf_alloc("%s_%d.VOB", fbase, fnum);
2298 } /*if*/
2299 outvob = open(fname, O_WRONLY | O_BINARY);
2300 if (outvob < 0)
2301 {
2302 fprintf
2303 (
2304 stderr,
2305 "\nERR: Error %d opening %s: %s\n",
2306 errno,
2307 fname,
2308 strerror(errno)
2309 );
2310 exit(1);
2311 } /*if*/
2312 free(fname);
2313 } /*if*/
2314 } /*if*/
2315
2316 memcpy(buf, thisvobu->sectdata, 0x26);
2317 write4(buf + 0x26, 0x100 + MPID_PRIVATE2); // private stream 2
2318 write2(buf + 0x2a, 0x3d4); // length
2319 buf[0x2c] = 0; /* substream ID, 0 = PCI */
2320 memset(buf + 0x2d,0, 0x400 - 0x2d);
2321 write4(buf + 0x400, 0x100 + MPID_PRIVATE2); // private stream 2
2322 write2(buf + 0x404, 0x3fa); // length
2323 buf[0x406] = 1; /* substream ID, 1 = DSI */
2324 memset(buf + 0x407, 0, 0x7ff - 0x407);
2325
2326 scr = readscr(buf + 4);
2327
2328 write4(buf + 0x2d, thisvobu->sector); /* sector number of this block */
2329 /* buf[0x35 .. 0x38] -- prohibited user ops -- none for now */
2330 write4(buf + 0x39, thisvobu->sectpts[0]); /* start presentation time (vobu_s_ptm) */
2331 write4(buf + 0x3d, thisvobu->sectpts[1]); /* end presentation time (vobu_e_ptm) */
2332 if (thisvobu->hasseqend) // if sequence_end_code
2333 write4(buf + 0x41, thisvobu->videopts[1]); // vobu_se_e_ptm
2334 write4
2335 (
2336 buf + 0x45,
2337 buildtimeeven
2338 (
2339 va,
2340 thisvobu->sectpts[0] - thisvob->vobu[thisvobu->firstvobuincell].sectpts[0]
2341 ) // total guess
2342 );
2343 /* c_eltm -- BCD cell elapsed time + frame rate */
2344
2345 if (thisvob->progchain->numbuttons)
2346 {
2347 /* fill in PCI packet with button info */
2348 const struct pgc * const pg = thisvob->progchain;
2349 int mask = getsubpmask(&va->vd), nrgrps, grp;
2350 char idmap[3];
2351
2352 write2(buf + 0x8d, 1); /* highlight status = all new highlight information for this VOBU */
2353 write4(buf + 0x8f, thisvob->vobu[0].sectpts[0]); /* highlight start time */
2354 write4(buf + 0x93, -1); /* highlight end time */
2355 write4(buf + 0x97, -1); /* button selection end time (ignore user after this) */
2356
2357 nrgrps = 0;
2358 write2(buf + 0x9b, 0); /* button groupings, none to begin with */
2359 for (j = 0; j < 4; j++)
2360 if (mask & (1 << j))
2361 {
2362 assert(nrgrps < 3);
2363 idmap[nrgrps] = j;
2364 write2
2365 (
2366 buf + 0x9b,
2367 read2(buf + 0x9b)
2368 +
2369 0x1000 /* add another button group */
2370 +
2371 (
2372 ((1 << j) >> 1)
2373 /* panscan/letterbox/normal bit for widescreen,
2374 0 for narrowscreen */
2375 <<
2376 (2 - nrgrps) * 4
2377 /* bit-shifted into appropriate button group nibble */
2378 )
2379 );
2380 nrgrps++;
2381 } /*if; for*/
2382 assert(nrgrps > 0);
2383
2384 buf[0x9e] = pg->numbuttons; /* number of buttons */
2385 buf[0x9f] = pg->numbuttons; /* number of numerically-selected buttons */
2386 memcpy(buf + 0xa3, thisvob->buttoncoli, 24);
2387 for (grp = 0; grp < nrgrps; grp++)
2388 {
2389 unsigned char *boffs = buf + 0xbb + 18 * (grp * 36 / nrgrps);
2390 /* divide BTN_IT entries equally among all groups -- does this matter? */
2391 const int sid = pg->subpmap[0][(int)idmap[grp]] & 127;
2392
2393 for (j = 0; j < pg->numbuttons; j++)
2394 /* fixme: no check against overrunning allocated portion of BTN_IT array? */
2395 {
2396 static unsigned char compilebuf[128 * 8], *rbuf;
2397 const struct button * const b = pg->buttons + j;
2398 const struct buttoninfo *bi;
2399 int k;
2400
2401 for (k = 0; k < b->numstream; k++)
2402 if (b->stream[k].substreamid == sid)
2403 break;
2404 if (k == b->numstream)
2405 continue; /* no matching button def for this substream */
2406 bi = &b->stream[k];
2407
2408 /* construct BTN_IT -- Button Information Table entry */
2409 boffs[0] = (bi->grp * 64) | (bi->x1 >> 4);
2410 boffs[1] = (bi->x1 << 4) | (bi->x2 >> 8);
2411 boffs[2] = bi->x2;
2412 boffs[3] = (bi->autoaction ? 64 : 0) | (bi->y1 >> 4);
2413 boffs[4] = (bi->y1 << 4) | (bi->y2 >> 8);
2414 boffs[5] = bi->y2;
2415 boffs[6] = findbutton(pg, bi->up, (j == 0) ? pg->numbuttons : j);
2416 boffs[7] = findbutton(pg, bi->down, (j + 1 == pg->numbuttons) ? 1 : j + 2);
2417 boffs[8] = findbutton(pg, bi->left, (j == 0) ? pg->numbuttons : j);
2418 boffs[9] = findbutton(pg, bi->right, (j + 1 == pg->numbuttons) ? 1 : j + 2);
2419 rbuf = vm_compile(compilebuf, compilebuf, ws, pg->pgcgroup, pg, b->commands, ismenu);
2420 if (rbuf - compilebuf == 8)
2421 {
2422 memcpy(boffs + 10, compilebuf, 8);
2423 }
2424 else if (allowallreg)
2425 {
2426 fprintf
2427 (
2428 stderr,
2429 "ERR: Button command is too complex to fit in one instruction,"
2430 " and allgprm==true.\n"
2431 );
2432 exit(1);
2433 }
2434 else
2435 write8(boffs + 10, 0x71, 0x01, 0x00, 0x0F, 0x00, j + 1, 0x00, 0x0d);
2436 // g[15] = j && linktailpgc
2437 /* transfer to full instruction sequence which will be
2438 generated by dvdpgc.c:genpgc */
2439 boffs += 18;
2440 } /*for j*/
2441 } /*for grp*/
2442 } /* if thisvob->progchain->numbuttons */
2443
2444 /* fill in DSI packet */
2445 write4(buf + 0x407, scr);
2446 write4(buf + 0x40b, thisvobu->sector); // current lbn
2447 if (thisvobu->numref > 0)
2448 {
2449 /* up to three reference frame relative end blocks */
2450 for (j = 0; j < thisvobu->numref; j++)
2451 write4(buf + 0x413 + j * 4, thisvobu->lastrefsect[j] - thisvobu->sector);
2452 for (; j < 3; j++) /* duplicate last one if less than 3 */
2453 write4(buf + 0x413 + j * 4, thisvobu->lastrefsect[thisvobu->numref - 1] - thisvobu->sector);
2454 } /*if*/
2455 write2(buf + 0x41f, thisvobu->vobcellid >> 8); /* VOB number */
2456 buf[0x422] = thisvobu->vobcellid; /* cell number within VOB */
2457 write4(buf + 0x423, read4(buf + 0x45)); /* cell elapsed time, BCD + frame rate */
2458 /* interleaved unit stuff not supported for now */
2459 write4(buf + 0x433, thisvob->vobu[0].sectpts[0]);
2460 /* time of first video frame in first GOP of VOB */
2461 write4(buf + 0x437, thisvob->vobu[thisvob->numvobus - 1].sectpts[1]);
2462 /* time of last video frame in last GOP of VOB */
2463 /* audio gap stuff not supported for now */
2464 /* seamless angle stuff not supported for now */
2465 write4
2466 (
2467 buf + 0x4f1,
2468 getsect(thisvob, vobuindex, findnextvideo(thisvob, vobuindex, 1), 0, 0xbfffffff)
2469 );
2470 /* offset to next VOBU with video */
2471 /* offset to next VOBU at various times forward filled in below */
2472 write4(buf + 0x541, getsect(thisvob, vobuindex, vobuindex + 1, 0, 0x3fffffff));
2473 /* offset to next VOBU */
2474 write4(buf + 0x545, getsect(thisvob, vobuindex, vobuindex - 1, 0, 0x3fffffff));
2475 /* offset to previous VOBU */
2476 /* offset to previous VOBU at various times backward filled in below */
2477 write4
2478 (
2479 buf + 0x595,
2480 getsect(thisvob, vobuindex, findnextvideo(thisvob, vobuindex, -1), 0, 0xbfffffff)
2481 );
2482 /* offset to previous VOBU with video */
2483 for (j = 0; j < va->numaudiotracks; j++)
2484 {
2485 int s = getaudch(va, j);
2486 if (s >= 0)
2487 s = findaudsect(thisvob, s, thisvobu->sectpts[0], thisvobu->sectpts[1]);
2488 if (s >= 0)
2489 {
2490 s = s - thisvobu->sector;
2491 if (s > 0x1fff || s < -(0x1fff))
2492 {
2493 fprintf
2494 (
2495 stderr,
2496 "\nWARN: audio sector out of range: %d (vobu #%d, pts ",
2497 s,
2498 vobuindex
2499 );
2500 printpts(thisvobu->sectpts[0]);
2501 fprintf(stderr, ")\n");
2502 s = 0;
2503 } /*if*/
2504 if (s < 0)
2505 s = (-s) | 0x8000; /* absolute value + backward-direction flag */
2506 }
2507 else
2508 s = 0x3fff; /* no more audio for this stream */
2509 write2(buf + 0x599 + j * 2, s);
2510 /* relative offset to first audio packet in this stream for this VOBU */
2511 } /*for*/
2512 for (j = 0; j < va->numsubpicturetracks; j++)
2513 {
2514 const struct audchannel * const ach = &thisvob->audch[j | 32];
2515 int s;
2516 if (ach->numaudpts)
2517 {
2518 int id = findspuidx(thisvob, j | 32, thisvobu->sectpts[0]);
2519 // if overlaps A, point to A
2520 // else if (A before here or doesn't exist) and (B after here or doesn't exist),
2521 // point to here
2522 // else point to B
2523 if
2524 (
2525 id >= 0
2526 &&
2527 ach->audpts[id].pts[0] < thisvobu->sectpts[1]
2528 &&
2529 ach->audpts[id].pts[1] >= thisvobu->sectpts[0]
2530 )
2531 s = findvobubysect(thisvob, ach->audpts[id].asect);
2532 else if
2533 (
2534 (id < 0 || ach->audpts[id].pts[1] < thisvobu->sectpts[0])
2535 &&
2536 (
2537 id + 1 == ach->numaudpts
2538 ||
2539 ach->audpts[id + 1].pts[0] >= thisvobu->sectpts[1]
2540 )
2541 )
2542 s = vobuindex;
2543 else
2544 s = findvobubysect(thisvob, ach->audpts[id + 1].asect);
2545 id = (s < vobuindex);
2546 s = getsect(thisvob, vobuindex, s, 0, 0x7fffffff) & 0x7fffffff;
2547 if (!s) /* same VOBU */
2548 s = 0x7fffffff;
2549 /* indicates current or later VOBU, no explicit forward offsets */
2550 if (s != 0x7fffffff && id)
2551 s |= 0x80000000; /* indicates offset to prior VOBU */
2552 }
2553 else
2554 s = 0; /* doesn't exist */
2555 write4(buf + 0x5a9 + j * 4, s);
2556 /* relative offset to VOBU (NAV pack) containing subpicture data
2557 for this stream for this VOBU */
2558 } /*for*/
2559 write4(buf + 0x40f, thisvobu->lastsector - thisvobu->sector);
2560 /* relative offset to last sector of VOBU */
2561 vff = vobuindex;
2562 vrew = vobuindex;
2563 for (j = 0; j < 19; j++)
2564 {
2565 /* fill in offsets to next/previous VOBUs at various time steps */
2566 int nff, nrew;
2567 nff = findvobu
2568 (
2569 thisvob,
2570 thisvobu->sectpts[0] + timeline[j] * DVD_FFREW_HALFSEC,
2571 thisvobu->firstvobuincell,
2572 thisvobu->lastvobuincell
2573 );
2574 // a hack -- the last vobu in the cell shouldn't have any forward ptrs
2575 // EXCEPT this hack violates both Grosse Pointe Blank and Bullitt -- what was I thinking?
2576 // if (i == thisvobu->lastvobuincell)
2577 // nff = i + 1;
2578 nrew = findvobu
2579 (
2580 thisvob,
2581 thisvobu->sectpts[0] - timeline[j] * DVD_FFREW_HALFSEC,
2582 thisvobu->firstvobuincell,
2583 thisvobu->lastvobuincell
2584 );
2585 /* note table entries are in order of decreasing time step */
2586 write4
2587 (
2588 buf + 0x53d - j * 4, /* forward jump */
2589 getsect(thisvob, vobuindex, nff, j >= 15 && nff > vff + 1, 0x3fffffff)
2590 );
2591 write4
2592 (
2593 buf + 0x549 + j * 4, /* backward jump */
2594 getsect(thisvob, vobuindex, nrew, j >= 15 && nrew < vrew - 1, 0x3fffffff)
2595 );
2596 vff = nff;
2597 vrew = nrew;
2598 } /*for*/
2599
2600 /* NAV pack all done, write it out */
2601 if (outvob != -1)
2602 {
2603 if (lseek(outvob, thisvobu->fsect * 2048, SEEK_SET) == (off_t)-1)
2604 /* where the NAV pack should go */
2605 {
2606 fprintf(stderr, "ERR: Error %d -- %s -- seeking in output VOB\n", errno, strerror(errno));
2607 exit(1);
2608 } /*if*/
2609 if (write(outvob, buf, 2048) != 2048)
2610 {
2611 fprintf
2612 (
2613 stderr,
2614 "ERR: Error %d -- %s -- writing NAV PACK to output VOB\n",
2615 errno,
2616 strerror(errno)
2617 );
2618 exit(1);
2619 } /*if*/
2620 /* update it */
2621 } /*if*/
2622 curvob++;
2623 if (!(curvob & 15)) /* time for another progress update */
2624 fprintf
2625 (
2626 stderr,
2627 "STAT: fixing VOBU at %dMB (%d/%d, %d%%)\r",
2628 thisvobu->sector / 512,
2629 curvob + 1,
2630 totvob,
2631 curvob * 100 / totvob
2632 );
2633 } /*for vobuindex*/
2634 } /*for pn*/
2635 if (outvob != -1)
2636 flushclose(outvob);
2637 if (totvob > 0)
2638 fprintf(stderr, "STAT: fixed %d VOBUs ", totvob);
2639 fprintf(stderr, "\n");
2640 } /*FixVobus*/
2641