1 /* MPEG-1/MPEG-2 parsing module for the Bitzi Bitcollider video plugin
2  *
3  * (PD) 2002 Mark Nelson [delirium] <delirium-bitzi@rufus.d2g.com>
4  * Please see file COPYING or http://bitzi.com/publicdomain for more
5  * information.
6  *
7  * The primary references used for the MPEG-1/2 stream formats were:
8  *     http://www.andrewduncan.ws/MPEG/MPEG-1_Picts.html and
9  *     http://www.andrewduncan.ws/MPEG/MPEG-2_Picts.html
10  */
11 
12 #include "video.h"
13 
14 /* Returns 1 or 2 to indicate MPEG-1 or MPEG-2
15  *
16  * Most MPEG data is stored in bits not necessarily aligned on byte boundaries;
17  * bits are ordered most-significant first, so big-endian of a sort.
18  * Block sizes only count bytes after the block size integer.
19  */
parse_mpeg(FILE * file,Data * data)20 int parse_mpeg(FILE *file, Data *data)
21 {
22    int version = 0;			/* MPEG-1/2; our return value */
23    uint32 temp;
24 
25    /* First check if this is a Program stream (multiplexed audio/video),
26     * and handle Pack header if so */
27    temp = fread_be(file, 4);
28    if(temp == 0x000001BA)
29    {
30       /* Figure out if this is an MPEG-1 or MPEG-2 program */
31       temp = (uint32) fgetc(file);
32       if((temp & 0xF0) == 0x20)		/* binary 0010 xxxx */
33 	 version = 1;
34       else if((temp & 0xC0) == 0x40)	/* binary 01xx xxxx */
35  	 version = 2;
36       else
37 	 return 0;
38 
39       if(version == 1)
40       {
41 	 fseek(file, 4L, SEEK_CUR);
42 	 data->bitrate = (unsigned int)
43 	    round_double((double)((fread_be(file, 3) & 0x7FFFFE) >> 1) * 0.4);
44       }
45       else
46       {
47 	 fseek(file, 5L, SEEK_CUR);
48 	 data->bitrate = (unsigned int)
49 	    round_double((double)((fread_be(file, 3) & 0xFFFFFC) >> 2) * 0.4);
50 
51 	 temp = fgetc(file) & 0x07;	/* stuffing bytes */
52 	 if(temp != 0)
53 	    fseek(file, (long) temp, SEEK_CUR);
54       }
55       /* Skip any other blocks we find until we get to a video stream, which
56        * might be within a 2nd PACK */
57       temp = fread_be(file, 4);
58       while(temp != 0x000001BA && temp != 0x000001E0)
59       {
60 	 if(feof(file))			/* shouldn't happen */
61 	    return version;
62 	 if(temp == 0x00000000)		/* Skip past zero padding */
63 	 {
64 	    while((temp & 0xFFFFFF00) != 0x00000100)
65 	    {
66 	       if(feof(file))
67 		  return version;	/* shouldn't happen here either */
68 	       temp <<= 8;
69 	       temp |= fgetc(file);
70 	    }
71 	 }
72 	 else
73 	 {
74 	    temp = fread_be(file, 2);
75 	    fseek(file, (long) temp, SEEK_CUR);
76 	    temp = fread_be(file, 4);
77 	 }
78       }
79 
80       /* Now read byte by byte until we find the 0x000001B3 instead of actually
81        * parsing (due to too many variations).  Theoretically this could mean
82        * we find 0x000001B3 as data inside another packet, but that's extremely
83        * unlikely, especially since the sequence header should not be far */
84       temp = fread_be(file, 4);
85       while(temp != 0x000001B3)
86       {
87 	 if(feof(file))			/* No seq. header; shouldn't happen */
88 	    return version;
89 	 temp <<= 8;
90 	 temp |= fgetc(file);
91       }
92    }
93    else					/* video stream only */
94       fseek(file, 4L, SEEK_SET);
95 
96    /* Now we're just past the video sequence header start code */
97 
98    temp = fread_be(file, 3);
99    data->width = (temp & 0xFFF000) >> 12;
100    data->height = temp & 0x000FFF;
101 
102    switch(fgetc(file) & 0x0F)
103    {
104    case 1:				/* 23.976 fps */
105    case 2:				/* 24 fps */
106       data->fps = 24;
107       break;
108    case 3:				/* 25 fps */
109       data->fps = 25;
110       break;
111    case 4:				/* 29.97 fps */
112    case 5:				/* 30 fps */
113       data->fps = 30;
114       break;
115    case 6:				/* 50 fps */
116       data->fps = 50;
117       break;
118    case 7:				/* 59.94 fps */
119    case 8:				/* 60 fps */
120       data->fps = 60;
121       break;
122    }
123 
124    if(data->bitrate == 0)		/* if this is a video-only stream, */
125    {					/* get bitrate from here */
126       temp = (fread_be(file, 3) & 0xFFFFC0) >> 6;
127       if(temp != 0x3FFFF)		/* variable bitrate */
128 	 data->bitrate = (unsigned int)
129 	    round_double((double) temp * 0.4);
130    }
131    else
132       fseek(file, 3L, SEEK_CUR);
133 
134    /* If MPEG-2 or don't know yet, look for the sequence header extension */
135    if(version != 1)
136    {
137       /* Skip past rest of sequence header and 64-byte matrices (if any) */
138       temp = fgetc(file);
139       if(temp & 0x02)
140       {
141 	 fseek(file, 63L, SEEK_CUR);
142 	 temp = fgetc(file);
143       }
144       if(temp & 0x01)
145 	 fseek(file, 64L, SEEK_CUR);
146 
147       temp = fread_be(file, 4);
148       if(temp == 0x000001B5)
149       {
150 	 if(version == 0)
151 	    version = 2;
152 
153 	 fseek(file, 1L, SEEK_CUR);
154 	 /* extensions specify MSBs of width/height */
155 	 temp = fread_be(file, 2);
156 	 data->width  |= (temp & 0x0180) << 5;
157 	 data->height |= (temp & 0x0060) << 7;
158 
159 	 fseek(file, 2L, SEEK_CUR);
160 	 /* and a numerator/denominator multiplier for fps */
161 	 temp = fgetc(file);
162 	 if((temp & 0x60) && (temp & 0x1F))
163 	    data->fps = (unsigned int)
164 	       round_double((double)data->fps * (temp & 0x60)/(temp & 0x1F));
165       }
166       else if(version == 0)
167 	 version = 1;
168    }
169 
170    return version;
171 }
172