1 /*
2     Utility to extract audio/video streams and dump information about
3     packetes in an MPEG stream.
4 */
5 /*
6  * Copyright (C) 2002 Scott Smith (trckjunky@users.sourceforge.net)
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or (at
11  * your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301 USA.
22  */
23 
24 #include "config.h"
25 
26 #include "compat.h"
27 
28 #include <setjmp.h>
29 #include <assert.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 
33 // this is needed for FreeBSD and Windows
34 #include <sys/time.h>
35 
36 #include "common.h"
37 
38 // #define SHOWDATA
39 
40 static unsigned int
41     inputpos = 0, /* position in stdin */
42     queuedlen = 0; /* total nr bytes awaiting writing to output files */
43 
44 const char * const frametype = "0IPB4567";
45 
46 #define SCRTIME 27000000
47 #define PTSTIME 90000
48 
49 #define WAITLEN (256*1024)
50 
51 #define BUFLEN (65536)
52 
53 struct fdbuf
54   {
55     int pos,len;
56     struct fdbuf *next;
57     unsigned char buf[BUFLEN];
58   };
59 
60 #define MAX_FILES 256
61   /* how many output files I can write at once -- can't have
62     more than this number of MPEG streams anyway */
63 
64 /* values for ofd.fd: */
65 #define FD_TOOPEN (-2) /* open file named ofd.fname on first write */
66 #define FD_CLOSED (-1)
67 
68 struct ofd
69   {
70     int fd;
71     char *fname;
72     struct fdbuf *firstbuf,**lastbufptr; /* queue of buffers awaiting writing */
73     int len;
74     bool isvalid;
75   } outputfds[256]; /* files to write, indexed by stream id */
76 
77 static int
78     ofdlist[MAX_FILES], /* indexes of used entries in outputfds */
79     numofd; /* contiguous used size of ofdlist */
80 
81 static int firstpts[256]; /* indexed by stream id */
82 
83 static bool
84     outputenglish = true,
85     nounknown = false,
86     closing = false,
87     outputmplex = false;
88 static int
89     audiodrop = 0;
90 
91 static fd_set
92   /* should be local to dowork routine */
93     rfd, wfd;
94 
readpts(const unsigned char * buf)95 static int64_t readpts(const unsigned char *buf)
96   {
97     return
98             (int64_t)((buf[0] & 0xf) >> 1) << 30
99         |
100             ((int64_t)buf[1] << 8 | buf[2]) >> 1 << 15
101         |
102             ((int64_t)buf[3] << 8 | buf[4]) >> 1;
103   } /*readpts*/
104 
hasbecomevalid(int stream,const struct ofd * o)105 static bool hasbecomevalid(int stream, const struct ofd *o)
106   /* checks if there is a valid packet header at the start of the data to be
107     written to the specified output stream. Assumes there is at least 4 bytes
108     of data waiting to be written. */
109   {
110     bool valid = false;
111     unsigned int realquad;
112     const struct fdbuf * const f1 = o->firstbuf;
113     if (f1 != 0)
114       {
115         unsigned char quad[4];
116         int i;
117         const struct fdbuf * const f2 = f1->next;
118         valid = true; /* next assumption */
119         for (i = 0; valid && i < 4; i++)
120           {
121             if (f1->len - f1->pos - i > 0)
122                 quad[i] = f1->buf[f1->pos + i];
123             else if (f2 != 0)
124                 quad[i] = f2->buf[f2->pos + i - (f1->len - f1->pos)];
125             else
126                 valid = false;
127           } /*for*/
128         if (valid)
129           {
130             realquad =
131                     quad[0] << 24
132                 |
133                     quad[1] << 16
134                 |
135                     quad[2] << 8
136                 |
137                     quad[3];
138           } /*if*/
139       } /*if*/
140     return
141             valid
142         &&
143             (
144                     stream >= MPID_AUDIO_FIRST
145                 &&
146                     stream <= MPID_AUDIO_LAST
147                 &&
148                     (realquad & 0xFFE00000) == 0xFFE00000
149             ||
150                     stream >= MPID_VIDEO_FIRST
151                 &&
152                     stream <= MPID_VIDEO_LAST
153                 &&
154                     realquad == 0x100 + MPID_SEQUENCE
155             );
156   } /*hasbecomevalid*/
157 
dowork(bool checkin)158 static bool dowork
159   (
160     bool checkin /* whether to check if bytes are available to be read from stdin */
161   )
162   /* writes pending packets to output streams. This is done concurrently to allow
163     for use with pipes. Returns true iff stdin has input available. */
164   {
165     int highestfd = -1;
166     struct timeval tv;
167     if (!numofd)
168         return checkin;
169     if (checkin)
170       {
171         FD_SET(STDIN_FILENO, &rfd);
172         highestfd = STDIN_FILENO;
173       }
174     else
175       {
176         FD_CLR(STDIN_FILENO, &rfd);
177       } /*if*/
178     while (true)
179       {
180         int i, minq = -1;
181         for (i = 0; i < numofd; i++)
182           {
183             struct ofd *o = &outputfds[ofdlist[i]];
184             if (o->fd != FD_CLOSED)
185               {
186                 if (o->fd == FD_TOOPEN)
187                   {
188                     int fd;
189                     fd = open(o->fname, O_CREAT | O_WRONLY | O_NONBLOCK, 0666);
190                     if (fd == -1 && errno == ENXIO)
191                       {
192                         continue; /* try again later, in case pipe not created yet */
193                       } /*if*/
194                     if (fd == -1)
195                       {
196                         fprintf(stderr,"Cannot open %s: %s\n",o->fname,strerror(errno));
197                         exit(1);
198                       } /*if*/
199                     o->fd = fd;
200                   } /*if*/
201                 // at this point, fd >= 0
202                 if (minq == -1 || o->len < minq)
203                   {
204                     minq = o->len;
205                   } /*if*/
206                 if ((o->len > 0 && o->isvalid) || o->len >= 4)
207                   {
208                     if (o->fd > highestfd)
209                         highestfd = o->fd;
210                     FD_SET(o->fd, &wfd);
211                   }
212                 else
213                   {
214                     FD_CLR(o->fd, &wfd);
215                     if (closing)
216                       {
217                         close(o->fd);
218                         o->fd = FD_CLOSED;
219                       } /*if*/
220                   } /*if*/
221               } /*if*/
222           } /*for*/
223         // if all the open files have more then WAITLEN bytes of data
224         // queued up, then don't process anymore
225         if (minq >= WAITLEN)
226           {
227             FD_CLR(STDIN_FILENO, &rfd);
228             break;
229           }
230         else if (minq >= 0 || outputmplex) // as long as one file is open, continue
231             break;
232         sleep(1);
233       } /*while*/
234     if (highestfd == -1)
235         return false; /* nothing to do */
236     tv.tv_sec = 1; // set timeout to 1 second just in case any files need to be opened
237     tv.tv_usec = 0;
238     if (select(highestfd + 1, &rfd, &wfd, NULL, &tv) > 0)
239       {
240         int i;
241         for (i = 0; i < numofd; i++)
242           {
243             struct ofd * const o = &outputfds[ofdlist[i]];
244             if (o->fd >= 0 && FD_ISSET(o->fd, &wfd))
245               {
246                 struct fdbuf * const f = o->firstbuf;
247                 int written;
248                 if (!o->isvalid && hasbecomevalid(ofdlist[i], o))
249                     o->isvalid = true;
250                 if (o->isvalid)
251                     written = write(o->fd, f->buf + f->pos, f->len - f->pos);
252                 else if (f->len - f->pos > 0)
253                     written = 1; /* discard one byte while waiting for valid packet */
254                 else
255                     written = 0;
256                 if (written == -1)
257                   {
258                     fprintf(stderr,"Error writing to fifo: %s\n",strerror(errno));
259                     exit(1);
260                   } /*if*/
261                 queuedlen -= written;
262                 f->pos += written;
263                 if (f->pos == f->len)
264                   {
265                   /* finished writing buffer at head of queue */
266                     o->firstbuf = f->next;
267                     if (o->lastbufptr == &f->next)
268                         o->lastbufptr = &o->firstbuf;
269                     free(f);
270                   } /*if*/
271                 o->len -= written;
272               } /*if*/
273           } /*for*/
274         if (FD_ISSET(STDIN_FILENO, &rfd))
275             return true;
276       } /*if*/
277     return false;
278   } /*dowork*/
279 
flushwork(void)280 static void flushwork(void)
281   /* flushes the work queue. */
282   {
283     closing = true;
284     while (queuedlen)
285         dowork(false);
286   } /*flushwork*/
287 
forceread(void * ptr,int len,bool required)288 static void forceread(void *ptr, int len, bool required)
289   /* reads the specified number of bytes from standard input, finishing processing
290     if EOF is reached. */
291   {
292     int nrbytes;
293     while (!dowork(true))
294       /* flush output queues while waiting for more input */;
295     nrbytes = fread(ptr, 1, len, stdin);
296     if (nrbytes != len)
297       {
298         bool success = true;
299         if (nrbytes < 0)
300           {
301             fprintf(stderr, "Error %d reading: %s\n", errno, strerror(errno));
302             success = false;
303           }
304         else if (nrbytes < len && required)
305           {
306             fprintf(stderr, "Unexpected read EOF\n");
307             success = false;
308           } /*if*/
309         flushwork();
310         exit(success ? 0 : 1);
311       } /*if*/
312     inputpos += len;
313   } /*forceread*/
314 
writetostream(int stream,unsigned char * buf,int len)315 static void writetostream(int stream, unsigned char *buf, int len)
316   /* queues more data to be written to the output file for the specified stream id,
317     if I am writing it. */
318   {
319     struct ofd * const o = &outputfds[stream];
320     if (o->fd == FD_CLOSED) /* not extracting this stream */
321         return;
322     while (len > 0)
323       {
324         int thislen;
325         struct fdbuf *fb;
326         if (!o->lastbufptr[0])
327           {
328             o->lastbufptr[0] = malloc(sizeof(struct fdbuf));
329             o->lastbufptr[0]->pos = 0;
330             o->lastbufptr[0]->len = 0;
331             o->lastbufptr[0]->next = 0;
332           } /*if*/
333         fb = o->lastbufptr[0];
334         thislen = BUFLEN - fb->len;
335         if (!thislen)
336           {
337             o->lastbufptr = &fb->next;
338             continue;
339           } /*if*/
340         if (thislen > len)
341             thislen = len;
342         o->len += thislen;
343         memcpy(fb->buf + fb->len, buf, thislen);
344         fb->len += thislen;
345         len -= thislen;
346         buf += thislen;
347         queuedlen += thislen;
348       } /*while*/
349   } /*writetostream*/
350 
process_packets(void (* readinput)(void * ptr,int len,bool required),bool recursed)351 static void process_packets
352   (
353     void (*readinput)(void *ptr, int len, bool required),
354     bool recursed
355   )
356   {
357     unsigned char hdr[4];
358     unsigned char buf[200];
359     bool fetchhdr = true;
360     while (true)
361       {
362         const int disppos = fetchhdr ? inputpos : inputpos - 4; /* where packet actually started */
363         bool handled = true;
364         int hdrid;
365         if (fetchhdr)
366           {
367             readinput(hdr, 4, false);
368           } /*if*/
369         fetchhdr = true; /* initial assumption */
370         hdrid =
371                 hdr[0] << 24
372             |
373                 hdr[1] << 16
374             |
375                 hdr[2] << 8
376             |
377                 hdr[3];
378         switch (hdrid)
379           {
380       // start codes:
381         case 0x100 + MPID_PICTURE: // picture header
382             readinput(buf, 4, true);
383             if (outputenglish)
384                 printf
385                   (
386                     "%08x: picture hdr, frametype=%c, temporal=%d\n",
387                     disppos,
388                     frametype[buf[1] >> 3 & 7],
389                     buf[0] << 2 | buf[1] >> 6
390                   );
391         break;
392 
393         case 0x100 + MPID_SEQUENCE: // sequence header
394             readinput(buf, 8, true);
395             if (outputenglish)
396                 printf
397                   (
398                     "%08x: sequence hdr: %dx%d, a/f:%02x, bitrate=%d\n",
399                     disppos,
400                     buf[0] << 4 | buf[1] >> 4,
401                     buf[1] << 8 & 0xf00 | buf[2],
402                     buf[3],
403                     buf[4] << 10 | buf[5] << 2 | buf[6] >> 6
404                   );
405             if (buf[7] & 2)
406                 readinput(buf + 8, 64, true);
407             if (buf[7] & 1)
408                 readinput(buf + 8, 64, true);
409         break;
410 
411         case 0x100 + MPID_EXTENSION: // extension header
412             readinput(buf, 1, true);
413             switch (buf[0] >> 4)
414               {
415             case 1:
416                 if (outputenglish)
417                     printf("%08x: sequence extension hdr\n", disppos);
418                 readinput(buf + 1, 5, true);
419             break;
420             case 2:
421                 if (outputenglish)
422                     printf("%08x: sequence display extension hdr\n", disppos);
423                 readinput(buf + 1, (buf[0] & 1) ? 7 : 3, true);
424             break;
425             case 7:
426                 if (outputenglish)
427                     printf("%08x: picture display extension hdr\n", disppos);
428             break;
429             case 8:
430                 readinput(buf + 1, 4, true);
431                 if (buf[4] & 64)
432                     readinput(buf + 5, 2, true);
433                 if (outputenglish)
434                   {
435                     printf
436                       (
437                         "%08x: picture coding extension hdr%s%s\n",
438                         disppos,
439                         (buf[3] & 0x80) ? ", top" : ", bottom",
440                         (buf[3] & 2) ? ", repeat" : ""
441                      );
442                   } /*if*/
443             break;
444             default:
445                 if (outputenglish)
446                     printf("%08x: extension hdr %x\n", disppos, buf[0] >> 4);
447             break;
448               } /*switch*/
449         break;
450 
451         case 0x100 + MPID_SEQUENCE_END: // end of sequence
452             if (outputenglish)
453                 printf("%08x: end of sequence\n", disppos);
454         break;
455 
456         case 0x100 + MPID_GOP: // group of pictures
457             readinput(buf, 4, true);
458             if (outputenglish)
459               {
460                 printf
461                   (
462                     "%08x: GOP: %s%d:%02d:%02d.%02d, %s%s\n",
463                     disppos,
464                     buf[0] & 128 ? "drop, " : "",
465                     buf[0] >> 2 & 31,
466                     (buf[0] << 4 | buf[1] >> 4) & 63,
467                     (buf[1] << 3 | buf[2] >> 5) & 63,
468                     (buf[2] << 1 | buf[3] >> 7) & 63,
469                     buf[3] & 64 ? "closed" : "open",
470                     buf[3] & 32 ? ", broken" : ""
471                     );
472               } /*if*/
473         break;
474 
475         case 0x100 + MPID_PROGRAM_END: // end of program stream
476             if (outputenglish)
477                 printf("%08x: end of program stream\n", disppos);
478         break;
479 
480         case 0x100 + MPID_PACK: // mpeg_pack_header
481           {
482             uint32_t scr,scrhi,scrext;
483             int64_t fulltime;
484             bool mpeg2 = true;
485             readinput(buf, 8, true);
486             if ((buf[0] & 0xC0) == 0x40)
487               {
488                 readinput(buf + 8, 2, true);
489                 scrhi = (buf[0] & 0x20) >> 5;
490                 scr =
491                         (buf[0] & 0x18) << 27
492                     |
493                         (buf[0] & 3) << 28
494                     |
495                         buf[1] << 20
496                     |
497                         (buf[2] & 0xf8) << 12
498                     |
499                         (buf[2] & 3) << 13
500                     |
501                         buf[3] << 5
502                     |
503                         (buf[4] & 0xf8) >> 3;
504                 scrext =
505                         (buf[4] & 3) << 7
506                     |
507                         buf[5] >> 1;
508                 if (scrext >= 300 && outputenglish)
509                   {
510                     printf("WARN: scrext in pack hdr > 300: %u\n", scrext);
511                   } /*if*/
512                 fulltime = (int64_t)scrhi << 32 | (int64_t)scr;
513                 fulltime *= 300;
514                 fulltime += scrext;
515                 mpeg2 = true;
516               }
517             else if ((buf[0] & 0xF0) == 0x20)
518               {
519                 mpeg2 = false;
520                 fulltime = readpts(buf);
521                 fulltime *= 300;
522               }
523             else
524               {
525                 if (outputenglish)
526                     printf("WARN: unknown pack header version\n");
527                 fulltime = 0;
528               } /*if*/
529             if (outputenglish)
530                 printf
531                   (
532                     "%08x: mpeg%c pack hdr, %" PRId64 ".%03" PRId64 " sec\n",
533                     disppos,
534                     mpeg2 ? '2' : '1',
535                     fulltime / SCRTIME,
536                     (fulltime % SCRTIME) / (SCRTIME / 1000)
537                   );
538           }
539         break;
540         default:
541             handled = false;
542         break;
543       } /*switch*/
544         if
545           (
546                 !handled
547             &&
548                 !recursed
549             &&
550                 (
551                     hdrid == 0x100 + MPID_SYSTEM
552                 ||
553                     hdrid == 0x100 + MPID_PRIVATE1
554                 ||
555                     hdrid == 0x100 + MPID_PAD
556                 ||
557                     hdrid == 0x100 + MPID_PRIVATE2
558                 ||
559                     hdrid >= 0x100 + MPID_AUDIO_FIRST && hdrid <= 0x100 + MPID_AUDIO_LAST
560                 ||
561                     hdrid >= 0x100 + MPID_VIDEO_FIRST && hdrid <= 0x100 + MPID_VIDEO_LAST
562                 )
563           )
564           {
565             bool has_extension = false;
566             unsigned int headerlen, packetlen, contentoffs;
567             int readlen;
568             bool dowrite = !recursed;
569             const int packetid = hdrid & 255;
570             if (outputenglish)
571                 printf("%08x: ", disppos);
572             if (packetid == MPID_SYSTEM)
573               {
574                 if (outputenglish)
575                     printf("system header");
576               }
577             else if (packetid == MPID_PRIVATE1)
578               {
579                 if (outputenglish)
580                     printf("pes private1");
581                 has_extension = true;
582               }
583             else if (packetid == MPID_PAD)
584               {
585                 if (outputenglish)
586                     printf("pes padding");
587               }
588             else if (packetid == MPID_PRIVATE2)
589               {
590                 if (outputenglish)
591                     printf("pes private2");
592               }
593             else if (packetid >= MPID_AUDIO_FIRST && packetid <= MPID_AUDIO_LAST)
594               {
595                 if (outputenglish)
596                     printf("pes audio %d", packetid - MPID_AUDIO_FIRST);
597                 if (audiodrop)
598                   {
599                     dowrite = false;
600                     audiodrop--;
601                   } /*if*/
602                 has_extension = true;
603               }
604             else if (packetid >= MPID_VIDEO_FIRST && packetid <= MPID_VIDEO_LAST)
605               {
606                 if (outputenglish)
607                     printf("pes video %d", packetid - MPID_VIDEO_FIRST);
608                 has_extension = true;
609               } /*if*/
610             readinput(buf, 2, true); // pes packet length
611             packetlen = buf[0] << 8 | buf[1];
612             readlen = packetlen > sizeof buf ? sizeof buf : packetlen;
613             readinput(buf, readlen, true);
614             packetlen -= readlen;
615             headerlen = buf[2]; /* length of packet header */
616             contentoffs = 3 + headerlen; /* beginning of packet content */
617             if (outputenglish)
618               {
619                 if (packetid == MPID_PRIVATE1) // private stream 1
620                   {
621                     const int sid = buf[contentoffs]; /* substream ID is first byte after header */
622                     switch (sid & 0xf8)
623                       {
624                     case 0x20:
625                     case 0x28:
626                     case 0x30:
627                     case 0x38:
628                         printf(", subpicture %d", sid & 0x1f);
629                     break;
630                     case 0x80:
631                         printf(", AC3 audio %d", sid & 7);
632                     break;
633                     case 0x88:
634                         printf(", DTS audio %d", sid & 7);
635                     case 0xa0:
636                         printf(", LPCM audio %d", sid & 7);
637                     break;
638                     default:
639                         printf(", substream id 0x%02x", sid);
640                     break;
641                       } /*switch*/
642                   }
643                 else if (packetid == MPID_PRIVATE2) // private stream 2
644                   {
645                     const int sid = buf[0];
646                     switch (sid)
647                       {
648                     case 0:
649                         printf(", PCI");
650                     break;
651                     case 1:
652                         printf(", DSI");
653                     break;
654                     default:
655                         printf(", substream id 0x%02x", sid);
656                     break;
657                       } /*switch*/
658                   } /*if*/
659                 printf("; length=%d", packetlen + readlen);
660                 if (has_extension)
661                   {
662                     int eptr;
663                     bool has_std = false, has_pts, has_dts;
664                     int hdroffs, std=0, std_scale=0;
665                     const bool mpeg2 = (buf[0] & 0xC0) == 0x80;
666                     if (mpeg2)
667                       {
668                         hdroffs = contentoffs;
669                         eptr = 3;
670                         has_pts = (buf[1] & 128) != 0;
671                         has_dts = (buf[1] & 64) != 0;
672                       }
673                     else
674                       {
675                         hdroffs = 0;
676                         while (hdroffs < sizeof(buf) && buf[hdroffs] == 0xff)
677                             hdroffs++;
678                         if ((buf[hdroffs] & 0xC0) == 0x40)
679                           {
680                             has_std = true;
681                             std_scale = (buf[hdroffs] & 32) ? 1024 : 128;
682                             std = ((buf[hdroffs] & 31) * 256 + buf[hdroffs + 1]) * std_scale;
683                             hdroffs += 2;
684                           } /*if*/
685                         eptr = hdroffs;
686                         has_pts = (buf[hdroffs] & 0xE0) == 0x20;
687                         has_dts = (buf[hdroffs] & 0xF0) == 0x30;
688                       } /*if*/
689                     printf("; hdr=%d", hdroffs);
690                     if (has_pts)
691                       {
692                         int64_t pts;
693                         pts = readpts(buf + eptr);
694                         eptr += 5;
695                         printf
696                           (
697                             "; pts %" PRId64 ".%03" PRId64 " sec",
698                             pts / PTSTIME,
699                             (pts % PTSTIME) / (PTSTIME / 1000)
700                           );
701                       } /*if*/
702                     if (has_dts)
703                       {
704                         int64_t dts;
705                         dts = readpts(buf + eptr);
706                         eptr += 5;
707                         printf
708                           (
709                             "; dts %" PRId64 ".%03" PRId64 " sec",
710                             dts / PTSTIME,
711                             (dts % PTSTIME) / (PTSTIME / 1000)
712                           );
713                       } /*if*/
714                     if (mpeg2)
715                       {
716                         if (buf[1] & 32)
717                           {
718                             printf("; escr");
719                             eptr += 6;
720                           } /*if*/
721                         if (buf[1] & 16)
722                           {
723                             printf("; es");
724                             eptr += 2;
725                           } /*if*/
726                         if (buf[1] & 4)
727                           {
728                             printf("; ci");
729                             eptr++;
730                           } /*if*/
731                         if (buf[1] & 2)
732                           {
733                             printf("; crc");
734                             eptr += 2;
735                           } /*if*/
736                         if (buf[1] & 1)
737                           {
738                             int pef = buf[eptr];
739                             eptr++;
740                             printf("; (pext)");
741                             if (pef & 128)
742                               {
743                                 printf("; user");
744                                 eptr += 16;
745                               } /*if*/
746                             if (pef & 64)
747                               {
748                                 printf("; pack");
749                                 eptr++;
750                               } /*if*/
751                             if (pef & 32)
752                               {
753                                 printf("; ppscf");
754                                 eptr += 2;
755                               } /*if*/
756                             if (pef & 16)
757                               {
758                                 std_scale = (buf[eptr] & 32) ? 1024 : 128;
759                                 printf
760                                   (
761                                     "; pstd=%d (scale=%d)",
762                                     ((buf[eptr] & 31) * 256 + buf[eptr + 1]) * std_scale,
763                                     std_scale
764                                   );
765                                 eptr += 2;
766                               } /*if*/
767                             if (pef & 1)
768                               {
769                                 printf("; (pext2)");
770                               /* eptr += 2; */ /* not further used */
771                               } /*if*/
772                           } /*if*/
773                       }
774                     else
775                       {
776                         if (has_std)
777                             printf("; pstd=%d (scale=%d)", std, std_scale);
778                       } /*if*/
779                   } /*if*/
780                 printf("\n");
781               } /*if*/
782             if (outputmplex && has_extension)
783               {
784                 if ((buf[1] & 128) != 0 && firstpts[packetid] == -1)
785                     firstpts[packetid] = readpts(buf + 3);
786                 if (firstpts[MPID_AUDIO_FIRST] != -1 && firstpts[MPID_VIDEO_FIRST] != -1)
787                   {
788                     printf("%d\n", firstpts[MPID_VIDEO_FIRST] - firstpts[MPID_AUDIO_FIRST]);
789                     fflush(stdout);
790                     close(1);
791                     outputmplex = false;
792                     if (!numofd)
793                         exit(0);
794                   } /*if*/
795               } /*if*/
796 #ifdef SHOWDATA
797             if (has_extension && outputenglish)
798               {
799                 int j;
800                 printf("  ");
801                 for (j=0; j<16; j++)
802                     printf(" %02x", buf[j + contentoffs]);
803                 printf("\n");
804               } /*if*/
805 #endif
806             if (!recursed)
807               {
808                 if (has_extension && dowrite)
809                   {
810                     writetostream(packetid, buf + contentoffs, readlen - contentoffs);
811                   } /*if*/
812 #if defined(HAVE_NESTED_ROUTINES)
813                 if (outputenglish && packetid >= MPID_VIDEO_FIRST && packetid <= MPID_VIDEO_LAST)
814                   {
815                   /* look inside PES packet to report on details of video packets */
816                     unsigned int remaining = readlen;
817                     jmp_buf resume;
818                   /* GCC extension! nested routine */
819                     void bufread(void *ptr, int len, bool required)
820                       {
821                         const unsigned int tocopy = remaining > len ? len : remaining;
822                         if (tocopy != 0)
823                           {
824                             memcpy(ptr, buf + contentoffs, tocopy);
825                             ptr = (unsigned char *)ptr + tocopy;
826                             len -= tocopy;
827                             contentoffs += tocopy;
828                             remaining -= tocopy;
829                             inputpos += tocopy;
830                           } /*if*/
831                         if (len != 0)
832                           {
833                           /* read more of packet */
834                             const unsigned int toread = packetlen < len ? packetlen : len;
835                             readinput(ptr, toread, required);
836                             if (dowrite)
837                               {
838                                 writetostream(packetid, ptr, toread);
839                               } /*if*/
840                             packetlen -= toread;
841                             len -= toread;
842                             if (len != 0)
843                               {
844                                 if (false /*required*/)
845                                   {
846                                     fprintf(stderr, "Unexpected nested read EOF\n");
847                                   } /*if*/
848                                 longjmp(resume, 1);
849                               } /*if*/
850                           } /*if*/
851                       } /*bufread*/
852                     inputpos -= remaining; /* rewind to start of packet content */
853                     if (!setjmp(resume))
854                       {
855                         process_packets(bufread, true);
856                       } /*if*/
857                   }
858                 else
859 #endif
860                   {
861                     while (packetlen != 0)
862                       {
863                         readlen = packetlen > sizeof buf ? sizeof(buf) : packetlen;
864                         readinput(buf, readlen, true);
865                         if (dowrite)
866                           {
867                             writetostream(packetid, buf, readlen);
868                           } /*if*/
869                         packetlen -= readlen;
870                       } /*while*/
871                   } /*if*/
872               } /*if*/
873             handled = true;
874           } /*if*/
875         if (!handled)
876           {
877             do
878               {
879                 if (!recursed && outputenglish && !nounknown)
880                     printf("%08x: unknown hdr: %08x\n", disppos, hdrid);
881                 hdr[0] = hdr[1];
882                 hdr[1] = hdr[2];
883                 hdr[2] = hdr[3];
884                 readinput(hdr + 3, 1, false);
885                 hdrid =
886                         hdr[0] << 24
887                     |
888                         hdr[1] << 16
889                     |
890                         hdr[2] << 8
891                     |
892                         hdr[3];
893               }
894             while ((hdrid & 0xffffff00) != 0x100);
895             fetchhdr = false; /* already got it */
896           } /*if*/
897       } /*while*/
898   } /*process_packets*/
899 
main(int argc,char ** argv)900 int main(int argc,char **argv)
901   {
902     bool skiptohdr = false;
903     fputs(PACKAGE_HEADER("mpeg2desc"), stderr);
904       {
905         int outputstream = 0, oc, i;
906         for (oc = 0; oc < 256; oc++)
907             outputfds[oc].fd = FD_CLOSED;
908         while (-1 != (oc = getopt(argc,argv,"ha:v:o:msd:u")))
909           {
910             switch (oc)
911               {
912             case 'd':
913                 audiodrop = strtounsigned(optarg, "audio drop count");
914             break;
915             case 'a':
916             case 'v':
917                 if (outputstream)
918                   {
919                     fprintf(stderr,"can only output one stream to stdout at a time\n; use -o to output more than\none stream\n");
920                     exit(1);
921                   } /*if*/
922                 outputstream = (oc == 'a' ? MPID_AUDIO_FIRST : MPID_VIDEO_FIRST) + strtounsigned(optarg, "stream id");
923             break;
924             case 'm':
925                 outputmplex = true;
926             break;
927             case 's':
928                 skiptohdr = true;
929             break;
930             case 'o':
931                 if (!outputstream)
932                   {
933                     fprintf(stderr,"no stream selected for '%s'\n",optarg);
934                     exit(1);
935                   } /*if*/
936                 outputfds[outputstream].fd = FD_TOOPEN;
937                 outputfds[outputstream].fname = optarg;
938                 outputstream = 0;
939             break;
940             case 'u':
941                 nounknown = true;
942             break;
943           // case 'h':
944             default:
945                 fprintf(stderr,
946                         "usage: mpeg2desc [options] < movie.mpg\n"
947                         "\t-a #: output audio stream # to stdout\n"
948                         "\t-v #: output video stream # to stdout\n"
949                         "\t-o FILE: output previous stream to FILE instead of stdout\n"
950                         "\t-s: skip to first valid header -- ensures mplex can handle output\n"
951                         "\t-m: output mplex offset to stdout\n"
952                         "\t-u: ignore unknown hdrs\n"
953                         "\t-h: help\n"
954                     );
955                 exit(1);
956             break;
957               } /*switch*/
958           } /*while*/
959         if (outputstream)
960           {
961             outputenglish = false;
962             outputfds[outputstream].fd = STDOUT_FILENO;
963           } /*if*/
964         if (outputmplex)
965           {
966             if (!outputenglish)
967               {
968                 fprintf(stderr,"Cannot output a stream and the mplex offset at the same time\n");
969                 exit(1);
970               } /*if*/
971             outputenglish = false;
972           } /*if*/
973         numofd = 0;
974         for (oc = 0; oc < MAX_FILES; oc++)
975             if (outputfds[oc].fd != -1)
976               {
977                 ofdlist[numofd++] = oc;
978                 outputfds[oc].firstbuf = 0;
979                 outputfds[oc].lastbufptr = &outputfds[oc].firstbuf;
980                 outputfds[oc].len = 0;
981                 outputfds[oc].isvalid = !skiptohdr;
982               } /*if; for*/
983         FD_ZERO(&rfd);
984         FD_ZERO(&wfd);
985         for (i = 0; i < 256; i++)
986           {
987             firstpts[i] = -1;
988           } /*for*/
989       }
990     process_packets(forceread, false);
991     return
992         0;
993   } /*main*/
994