Doxygen Source Code Documentation
        
Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Search   
extract_mpeg2.c
Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 #include "config.h"
00025 
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <errno.h>
00030 #include <getopt.h>
00031 #ifdef HAVE_IO_H
00032 #include <fcntl.h>
00033 #include <io.h>
00034 #endif
00035 #include <inttypes.h>
00036 
00037 #define BUFFER_SIZE 4096
00038 static uint8_t buffer[BUFFER_SIZE];
00039 static FILE * in_file;
00040 static int demux_track = 0xe0;
00041 static int demux_pid = 0;
00042 static int demux_pva = 0;
00043 
00044 static void print_usage (char ** argv)
00045 {
00046     fprintf (stderr, "usage: %s [-s <track>] [-t <pid>] [-p] <file>\n"
00047              "\t-s\tset track number (0-15 or 0xe0-0xef)\n"
00048              "\t-t\tuse transport stream demultiplexer, pid 0x10-0x1ffe\n"
00049              "\t-p\tuse pva demultiplexer\n",
00050              argv[0]);
00051 
00052     exit (1);
00053 }
00054 
00055 static void handle_args (int argc, char ** argv)
00056 {
00057     int c;
00058     char * s;
00059 
00060     while ((c = getopt (argc, argv, "s:t:p")) != -1)
00061         switch (c) {
00062         case 's':
00063             demux_track = strtol (optarg, &s, 0);
00064             if (demux_track < 0xe0)
00065                 demux_track += 0xe0;
00066             if ((demux_track < 0xe0) || (demux_track > 0xef) || (*s)) {
00067                 fprintf (stderr, "Invalid track number: %s\n", optarg);
00068                 print_usage (argv);
00069             }
00070             break;
00071 
00072         case 't':
00073             demux_pid = strtol (optarg, &s, 0);
00074             if ((demux_pid < 0x10) || (demux_pid > 0x1ffe) || (*s)) {
00075                 fprintf (stderr, "Invalid pid: %s\n", optarg);
00076                 print_usage (argv);
00077             }
00078             break;
00079 
00080         case 'p':
00081             demux_pva = 1;
00082             break;
00083 
00084         default:
00085             print_usage (argv);
00086         }
00087 
00088     if (optind < argc) {
00089         in_file = fopen (argv[optind], "rb");
00090         if (!in_file) {
00091             fprintf (stderr, "%s - could not open file %s\n", strerror (errno),
00092                      argv[optind]);
00093             exit (1);
00094         }
00095     } else
00096         in_file = stdin;
00097 }
00098 
00099 #define DEMUX_PAYLOAD_START 1
00100 static int demux (uint8_t * buf, uint8_t * end, int flags)
00101 {
00102     static int mpeg1_skip_table[16] = {
00103         0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
00104     };
00105 
00106     
00107 
00108 
00109 
00110 
00111 
00112 
00113 
00114 
00115 
00116 
00117 
00118 
00119 
00120 
00121 
00122 #define DEMUX_HEADER 0
00123 #define DEMUX_DATA 1
00124 #define DEMUX_SKIP 2
00125     static int state = DEMUX_SKIP;
00126     static int state_bytes = 0;
00127     static uint8_t head_buf[264];
00128 
00129     uint8_t * header;
00130     int bytes;
00131     int len;
00132 
00133 #define NEEDBYTES(x)                                            \
00134     do {                                                        \
00135         int missing;                                            \
00136                                                                 \
00137         missing = (x) - bytes;                                  \
00138         if (missing > 0) {                                      \
00139             if (header == head_buf) {                           \
00140                 if (missing <= end - buf) {                     \
00141                     memcpy (header + bytes, buf, missing);      \
00142                     buf += missing;                             \
00143                     bytes = (x);                                \
00144                 } else {                                        \
00145                     memcpy (header + bytes, buf, end - buf);    \
00146                     state_bytes = bytes + end - buf;            \
00147                     return 0;                                   \
00148                 }                                               \
00149             } else {                                            \
00150                 memcpy (head_buf, header, bytes);               \
00151                 state = DEMUX_HEADER;                           \
00152                 state_bytes = bytes;                            \
00153                 return 0;                                       \
00154             }                                                   \
00155         }                                                       \
00156     } while (0)
00157 
00158 #define DONEBYTES(x)            \
00159     do {                        \
00160         if (header != head_buf) \
00161             buf = header + (x); \
00162     } while (0)
00163 
00164     if (flags & DEMUX_PAYLOAD_START)
00165         goto payload_start;
00166     switch (state) {
00167     case DEMUX_HEADER:
00168         if (state_bytes > 0) {
00169             header = head_buf;
00170             bytes = state_bytes;
00171             goto continue_header;
00172         }
00173         break;
00174     case DEMUX_DATA:
00175         if (demux_pid || (state_bytes > end - buf)) {
00176             fwrite (buf, end - buf, 1, stdout);
00177             state_bytes -= end - buf;
00178             return 0;
00179         }
00180         fwrite (buf, state_bytes, 1, stdout);
00181         buf += state_bytes;
00182         break;
00183     case DEMUX_SKIP:
00184         if (demux_pid || (state_bytes > end - buf)) {
00185             state_bytes -= end - buf;
00186             return 0;
00187         }
00188         buf += state_bytes;
00189         break;
00190     }
00191 
00192     while (1) {
00193         if (demux_pid) {
00194             state = DEMUX_SKIP;
00195             return 0;
00196         }
00197     payload_start:
00198         header = buf;
00199         bytes = end - buf;
00200     continue_header:
00201         NEEDBYTES (4);
00202         if (header[0] || header[1] || (header[2] != 1)) {
00203             if (demux_pid) {
00204                 state = DEMUX_SKIP;
00205                 return 0;
00206             } else if (header != head_buf) {
00207                 buf++;
00208                 goto payload_start;
00209             } else {
00210                 header[0] = header[1];
00211                 header[1] = header[2];
00212                 header[2] = header[3];
00213                 bytes = 3;
00214                 goto continue_header;
00215             }
00216         }
00217         if (demux_pid) {
00218             if ((header[3] >= 0xe0) && (header[3] <= 0xef))
00219                 goto pes;
00220             fprintf (stderr, "bad stream id %x\n", header[3]);
00221             exit (1);
00222         }
00223         switch (header[3]) {
00224         case 0xb9:      
00225             
00226             
00227             return 1;
00228         case 0xba:      
00229             NEEDBYTES (12);
00230             if ((header[4] & 0xc0) == 0x40) {   
00231                 NEEDBYTES (14);
00232                 len = 14 + (header[13] & 7);
00233                 NEEDBYTES (len);
00234                 DONEBYTES (len);
00235                 
00236             } else if ((header[4] & 0xf0) == 0x20) {    
00237                 DONEBYTES (12);
00238                 
00239             } else {
00240                 fprintf (stderr, "weird pack header\n");
00241                 exit (1);
00242             }
00243             break;
00244         default:
00245             if (header[3] == demux_track) {
00246             pes:
00247                 NEEDBYTES (7);
00248                 if ((header[6] & 0xc0) == 0x80) {       
00249                     NEEDBYTES (9);
00250                     len = 9 + header[8];
00251                     NEEDBYTES (len);
00252                     
00253                 } else {        
00254                     len = 7;
00255                     while ((header-1)[len] == 0xff) {
00256                         len++;
00257                         NEEDBYTES (len);
00258                         if (len > 23) {
00259                             fprintf (stderr, "too much stuffing\n");
00260                             break;
00261                         }
00262                     }
00263                     if (((header-1)[len] & 0xc0) == 0x40) {
00264                         len += 2;
00265                         NEEDBYTES (len);
00266                     }
00267                     len += mpeg1_skip_table[(header - 1)[len] >> 4];
00268                     NEEDBYTES (len);
00269                     
00270                 }
00271                 DONEBYTES (len);
00272                 bytes = 6 + (header[4] << 8) + header[5] - len;
00273                 if (demux_pid || (bytes > end - buf)) {
00274                     fwrite (buf, end - buf, 1, stdout);
00275                     state = DEMUX_DATA;
00276                     state_bytes = bytes - (end - buf);
00277                     return 0;
00278                 } else if (bytes <= 0)
00279                     continue;
00280                 fwrite (buf, bytes, 1, stdout);
00281                 buf += bytes;
00282             } else if (header[3] < 0xb9) {
00283                 fprintf (stderr,
00284                          "looks like a video stream, not system stream\n");
00285                 DONEBYTES (4);
00286             } else {
00287                 NEEDBYTES (6);
00288                 DONEBYTES (6);
00289                 bytes = (header[4] << 8) + header[5];
00290                 if (bytes > end - buf) {
00291                     state = DEMUX_SKIP;
00292                     state_bytes = bytes - (end - buf);
00293                     return 0;
00294                 }
00295                 buf += bytes;
00296             }
00297         }
00298     }
00299 }
00300 
00301 static void ps_loop (void)
00302 {
00303     uint8_t * end;
00304 
00305     do {
00306         end = buffer + fread (buffer, 1, BUFFER_SIZE, in_file);
00307         if (demux (buffer, end, 0))
00308             break;      
00309     } while (end == buffer + BUFFER_SIZE);
00310 }
00311 
00312 static int pva_demux (uint8_t * buf, uint8_t * end)
00313 {
00314     static int state = DEMUX_SKIP;
00315     static int state_bytes = 0;
00316     static uint8_t head_buf[12];
00317 
00318     uint8_t * header;
00319     int bytes;
00320     int len;
00321 
00322     switch (state) {
00323     case DEMUX_HEADER:
00324         if (state_bytes > 0) {
00325             header = head_buf;
00326             bytes = state_bytes;
00327             goto continue_header;
00328         }
00329         break;
00330     case DEMUX_DATA:
00331         if (state_bytes > end - buf) {
00332             fwrite (buf, end - buf, 1, stdout);
00333             state_bytes -= end - buf;
00334             return 0;
00335         }
00336         fwrite (buf, state_bytes, 1, stdout);
00337         buf += state_bytes;
00338         break;
00339     case DEMUX_SKIP:
00340         if (state_bytes > end - buf) {
00341             state_bytes -= end - buf;
00342             return 0;
00343         }
00344         buf += state_bytes;
00345         break;
00346     }
00347 
00348     while (1) {
00349     payload_start:
00350         header = buf;
00351         bytes = end - buf;
00352     continue_header:
00353         NEEDBYTES (2);
00354         if (header[0] != 0x41 || header[1] != 0x56) {
00355             if (header != head_buf) {
00356                 buf++;
00357                 goto payload_start;
00358             } else {
00359                 header[0] = header[1];
00360                 bytes = 1;
00361                 goto continue_header;
00362             }
00363         }
00364         NEEDBYTES (8);
00365         if (header[2] != 1) {
00366             DONEBYTES (8);
00367             bytes = (header[6] << 8) + header[7];
00368             if (bytes > end - buf) {
00369                 state = DEMUX_SKIP;
00370                 state_bytes = bytes - (end - buf);
00371                 return 0;
00372             } 
00373             buf += bytes; 
00374         } else {
00375             len = 8;
00376             if (header[5] & 0x10) {
00377                 len = 12;
00378                 NEEDBYTES (len);
00379             }
00380             DONEBYTES (len);
00381             bytes = (header[6] << 8) + header[7] + 8 - len;
00382             if (bytes > end - buf) {
00383                 fwrite (buf, end - buf, 1, stdout);
00384                 state = DEMUX_DATA;
00385                 state_bytes = bytes - (end - buf);
00386                 return 0;
00387             } else if (bytes > 0) {
00388                 fwrite (buf, bytes, 1, stdout);
00389                 buf += bytes;
00390             }
00391         }
00392     }
00393 }
00394 
00395 static void pva_loop (void)
00396 {
00397     uint8_t * end;
00398 
00399     do {
00400         end = buffer + fread (buffer, 1, BUFFER_SIZE, in_file);
00401         pva_demux (buffer, end);
00402     } while (end == buffer + BUFFER_SIZE);
00403 }
00404 
00405 static void ts_loop (void)
00406 {
00407 #define PACKETS (BUFFER_SIZE / 188)
00408     uint8_t * buf;
00409     uint8_t * data;
00410     uint8_t * end;
00411     int packets;
00412     int i;
00413     int pid;
00414 
00415     do {
00416         packets = fread (buffer, 188, PACKETS, in_file);
00417         for (i = 0; i < packets; i++) {
00418             buf = buffer + i * 188;
00419             end = buf + 188;
00420             if (buf[0] != 0x47) {
00421                 fprintf (stderr, "bad sync byte\n");
00422                 exit (1);
00423             }
00424             pid = ((buf[1] << 8) + buf[2]) & 0x1fff;
00425             if (pid != demux_pid)
00426                 continue;
00427             data = buf + 4;
00428             if (buf[3] & 0x20) {        
00429                 data = buf + 5 + buf[4];
00430                 if (data > end)
00431                     continue;
00432             }
00433             if (buf[3] & 0x10)
00434                 demux (data, end, (buf[1] & 0x40) ? DEMUX_PAYLOAD_START : 0);
00435         }
00436     } while (packets == PACKETS);
00437 }
00438 
00439 int main (int argc, char ** argv)
00440 {
00441 #ifdef HAVE_IO_H
00442     setmode (fileno (stdout), O_BINARY);
00443 #endif
00444 
00445     handle_args (argc, argv);
00446 
00447     if (demux_pva)
00448         pva_loop ();
00449     if (demux_pid)
00450         ts_loop ();
00451     else
00452         ps_loop ();
00453 
00454     return 0;
00455 }