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