1 /* show QuickTime .mov file structure (C) 2001. by A'rpi/ESP-team
2 * various hacks by alex@naxine.org
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 /*
20 Blocks: 4bytes atom_size
21 4bytes atom_type (name)
22 ...
23
24 By older files, mdat is at the beginning, and moov follows it later,
25 by newer files, moov is at the begininng.
26
27 Fontosabb typeok:
28
29 trak: track: ezeken belul van egy-egy stream (video/audio)
30 tkhd: track header: fps (video esten picture size is itt van)
31 vmhd: video media handler (video stream informaciok)
32 smhd: sound media handler (audio stream informaciok)
33 */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37
38 #undef NO_SPECIAL
39
atom2human_type(int type)40 static char *atom2human_type(int type)
41 {
42 switch (type)
43 {
44 case 0x766F6F6D: return "Information sections"; /* moov */
45 case 0x6468766D: return "Movie header"; /* mvhd */
46 case 0x6169646D: return "Media stream"; /* mdia */
47 case 0x64686D76: return "Video media header"; /* vmhd */
48 case 0x64686D73: return "Sound media header"; /* smhd */
49 case 0x6468646D: return "Media header"; /* mdhd */
50 case 0x666E696D: return "Media information"; /* minf */
51 case 0x726C6468: return "Handler reference"; /* hdlr */
52 case 0x6B617274: return "New track (stream)"; /* trak */
53 case 0x75716D72: return "rmqu";
54 case 0x65657266: return "free";
55 case 0x64686B74: return "Track header"; /* tkhd */
56 case 0x61746475: return "User data"; /* udta */
57 case 0x7461646D: return "Movie data"; /* mdat */
58 case 0x6C627473: return "Sample information table"; /* stbl */
59 case 0x64737473: return "Sample description"; /* stsd */
60 case 0x6F637473: return "Chunk offset table"; /* stco */
61 case 0x73747473: return "Sample time table"; /* stts */
62 case 0x63737473: return "Sample->Chunk mapping table"; /* stsc */
63 case 0x7A737473: return "Sample size table"; /* stsz */
64 }
65 return "unknown";
66 }
67
68 #define S_NONE 0
69 #define S_AUDIO 1
70 #define S_VIDEO 2
71 int stream = S_NONE;
72 int v_stream = 0;
73 int a_stream = 0;
74
read_dword(FILE * f)75 static unsigned int read_dword(FILE *f){
76 unsigned char atom_size_b[4];
77 if(fread(&atom_size_b,4,1,f)<=0) return -1;
78 return (atom_size_b[0]<<24)|(atom_size_b[1]<<16)|(atom_size_b[2]<<8)|atom_size_b[3];
79 }
80
video_stream_info(FILE * f,int len)81 static void video_stream_info(FILE *f, int len)
82 {
83 int orig_pos = ftell(f);
84 unsigned char data[len-8];
85 int i;
86 // char codec[len-8];
87
88 len -= 8;
89 for (i=0; i<len; i++)
90 fread(&data[i], 1, 1, f);
91
92 // strncpy(codec, &data[43], len-43);
93 // printf(" [codec: %s]\n", &codec);
94 fseek(f,orig_pos,SEEK_SET);
95 }
96
audio_stream_info(FILE * f,int len)97 static void audio_stream_info(FILE *f, int len)
98 {
99 int orig_pos = ftell(f);
100 unsigned char data[len-8];
101 int i;
102
103 len -= 8;
104 for (i=0; i<len; i++)
105 fread(&data[i], 1, 1, f);
106
107 printf(" [%d bit", data[19]);
108 if (data[17] == 1)
109 printf(" mono");
110 else
111 printf(" %d channels", data[17]);
112 printf("]\n");
113 fseek(f,orig_pos,SEEK_SET);
114 }
115
116 #if 0
117 static void userdata_info(FILE *f, int len, int pos, int level)
118 {
119 int orig_pos = pos; /*ftell(f);*/
120 unsigned int atom_size = 1;
121 unsigned int atom_type;
122
123 // printf("userdata @ %d:%d (%d)\n", pos, pos+len, len);
124
125 // fseek(f, pos+3, SEEK_SET);
126
127 while (atom_size != 0)
128 {
129 atom_size=read_dword(f);// if(fread(&atom_size_b,4,1,f)<=0) break;
130 if(fread(&atom_type,4,1,f)<=0) break;
131
132 if(atom_size<8) break; // error
133
134 // printf("%08X: %*s %.4s (%08X) %05d (begin: %08X)\n",pos,level*2,"",
135 // &atom_type,atom_type,atom_size,pos+8);
136
137 switch(atom_type)
138 {
139 case 0x797063A9: /* cpy (copyright) */
140 {
141 char *data = malloc(atom_size-8);
142
143 fseek(f, pos+6, SEEK_SET);
144 fread(data, atom_size-8, 1, f);
145 printf(" Copyright: %s\n", data);
146 free(data);
147 }
148 break;
149 case 0x666E69A9: /* inf (information) */
150 {
151 char data[atom_size-8];
152
153 fread(&data, 1, atom_size-8, f);
154 printf(" Owner: %s\n", &data);
155 }
156 break;
157 case 0x6D616EA9: /* nam (name) */
158 {
159 char data[atom_size-8];
160
161 fread(&data, 1, atom_size-8, f);
162 printf(" Name: %s\n", &data);
163 }
164 break;
165 }
166 }
167 fseek(f,orig_pos,SEEK_SET);
168 }
169 #endif
170
171 int time_scale = 0;
172
lschunks(FILE * f,int level,unsigned int endpos)173 static void lschunks(FILE *f,int level,unsigned int endpos){
174 unsigned int atom_size;
175 unsigned int atom_type;
176 int pos;
177
178 while(endpos==0 || ftell(f)<endpos){
179 pos=ftell(f);
180 atom_size=read_dword(f);// if(fread(&atom_size_b,4,1,f)<=0) break;
181 if(fread(&atom_type,4,1,f)<=0) break;
182
183 if(atom_size<8) break; // error
184
185 printf("%08X: %*s %.4s (%08X) %05d [%s] (begin: %08X)\n",
186 pos, level * 2, "", &atom_type, atom_type,
187 atom_size, atom2human_type(atom_type),
188 pos + 8); // 8: atom_size fields (4) + atom_type fields (4)
189
190 #ifndef NO_SPECIAL
191 // if (atom_type == 0x61746475)
192 // userdata_info(f, atom_size, pos, level);
193
194 if (atom_type == 0x6468646D)
195 {
196 char data[4];
197
198 fread(&data, 1, 1, f); // char
199 printf("mdhd version %d\n", data[0]);
200 fread(&data, 3, 1, f); // int24
201 fread(&data, 4, 1, f); // int32
202 fread(&data, 4, 1, f); // int32
203 fread(&data, 4, 1, f); // int32
204 time_scale = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
205 printf("timescale: %d\n", time_scale);
206 fread(&data, 4, 1, f); // int32
207 fread(&data, 2, 1, f); // int16
208 fread(&data, 2, 1, f); // int16
209 }
210
211 if (atom_type == 0x64686D76)
212 {
213 stream = S_VIDEO;
214 printf(" Found VIDEO Stream #%d\n", v_stream++);
215 }
216
217 if (atom_type == 0x64686D73)
218 {
219 stream = S_AUDIO;
220 printf(" Found AUDIO Stream #%d\n", a_stream++);
221 }
222
223 if (atom_type == 0x64686B74) // tkhd - track header
224 {
225 int i;
226 unsigned char data[atom_size];
227 int x, y;
228
229 for (i=0; i<atom_size; i++)
230 fread(&data[i], 1, 1, f);
231
232 x = data[77];
233 y = data[81];
234 printf(" Flags: %d\n", data[3]);
235 printf(" Picture size: %dx%d\n", x, y);
236 if (x == 0 && y == 0)
237 printf(" Possible audio stream!\n");
238 }
239
240 if(atom_type==0x64737473) { // stsd
241 unsigned int tmp;
242 unsigned int count;
243 int i;
244 fread(&tmp,4,1,f);
245 count=read_dword(f);// fread(&count,4,1,f);
246 printf("desc count = %d\n",count);
247 for(i=0;i<count;i++){
248 unsigned int len;
249 unsigned int format;
250 len=read_dword(f); // fread(&len,4,1,f);
251 fread(&format,4,1,f);
252 printf(" desc #%d: %.4s (%d)\n",i+1,&format,len);
253 if (stream == S_VIDEO)
254 video_stream_info(f, len);
255 if (stream == S_AUDIO)
256 audio_stream_info(f, len);
257 fseek(f,len-8,SEEK_CUR);
258 }
259 }
260
261 if(atom_type==0x6F637473) { // stco
262 int len,i;
263 read_dword(f);
264 len=read_dword(f);
265 printf("Chunk table size :%d\n",len);
266 for(i=0;i<len;i++) printf(" chunk #%d: 0x%X\n",i+1,read_dword(f));
267 }
268
269
270 if(atom_type==0x73747473) { // stts
271 int len,i;
272 read_dword(f);
273 len=read_dword(f);
274 printf("T->S table size :%d\n",len);
275 for(i=0;i<len;i++){
276 int num=read_dword(f);
277 int dur=read_dword(f);
278 printf("%5d samples: %d duration", num, dur);
279 if (stream == S_AUDIO)
280 printf("(rate: %f Hz)\n", (float)time_scale/dur);
281 else
282 printf("(fps: %f)\n", (float)time_scale/dur);
283 }
284 }
285
286 if(atom_type==0x63737473) { // stsc
287 int len,i;
288 read_dword(f);
289 len=read_dword(f);
290 printf("S->C table size :%d\n",len);
291 for(i=0;i<len;i++){
292 int first=read_dword(f);
293 int spc=read_dword(f);
294 int sdid=read_dword(f);
295 printf(" chunk %d... %d s/c desc: %d\n",first,spc,sdid);
296 }
297 }
298
299 if(atom_type==0x7A737473) { // stsz
300 int len,i,ss;
301 read_dword(f);
302 ss=read_dword(f);
303 len=read_dword(f);
304 printf("Sample size table len: %d\n",len);
305 if(ss){
306 printf(" common sample size: %d bytes\n",ss);
307 } else {
308 for(i=0;i<len;i++) printf(" sample #%d: %d bytes\n",i+1,read_dword(f));
309 }
310 }
311 #endif
312
313 #if 1
314 switch(atom_type){
315 case 0x7461646D: // mdat Movie data
316 case 0x75716D72: // rmqu
317 case 0x65657266: // free JUNK
318 case 0x64686B74: // tkhd Track header
319 case 0x61746475: // udta User data
320 case 0x64737473: // stsd Sample description
321 case 0x6F637473: // stco Chunk offset table
322 case 0x73747473: // stts Sample time table
323 case 0x63737473: // stsc Sample->Chunk mapping table
324 case 0x7A737473: // stsz Sample size table
325 case 0x746f6e70: // pnot
326 case 0x54434950: // PICT
327 case 0x70797466:
328 break;
329 default: lschunks(f,level+1,pos+atom_size);
330 }
331 #else
332 switch(atom_type){
333 case 0x766F6F6D: // moov
334 case 0x61726D72: // rmra
335 case 0x61646D72: // rmda
336 lschunks(f,level+1,pos+atom_size);
337 }
338 #endif
339 fseek(f,pos+atom_size,SEEK_SET);
340 }
341 }
342
main(int argc,char * argv[])343 int main(int argc,char* argv[])
344 {
345 FILE *f;
346
347 if ((f = fopen(argc>1?argv[1]:"Akira.mov","rb")) == NULL)
348 return 1;
349
350 printf("%.8s %.4s (%.8s) %5s [%s]\n\n",
351 "position", "atom", "atomtype", "len", "human readable atom name");
352
353 lschunks(f, 0, 0);
354
355 printf("\nSummary: streams: %d video/%d audio\n", v_stream, a_stream);
356
357 return 0;
358 }
359