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