1 /* QuickTime 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 reference for the QuickTime file format is:
8  *     http://developer.apple.com/techpubs/quicktime/qtdevdocs/QTFF/qtff.html
9  */
10 
11 #include "video.h"
12 
13 /* QuickTime uses big-endian ordering, and block ("atom") lengths include the
14  * entire atom, including the fourcc specifying atom type and the length
15  * integer itself.
16  */
parse_quicktime(FILE * file,Data * data)17 void parse_quicktime(FILE *file, Data *data)
18 {
19    char fourcc[5];
20    unsigned blockLen;
21    unsigned subBlockLen;
22    unsigned subSubBlockLen;
23    unsigned timescale;
24    long blockStart;
25    long subBlockStart;
26    long subSubBlockStart;
27 
28    fseek(file, 4L, SEEK_SET);
29    fread(fourcc, sizeof(char), 4, file);
30    /* If data is first, header's at end of file, so skip to it */
31    if(memcmp(fourcc, "mdat", 4)==0)
32    {
33       fseek(file, 0L, SEEK_SET);
34       blockLen = fread_be(file, 4);
35       fseek(file, (long) (blockLen + 4), SEEK_SET);
36       fread(fourcc, sizeof(char), 4, file);
37    }
38 
39    if(memcmp(fourcc, "moov", 4)!=0)
40       return;
41    blockStart = ftell(file);
42    blockLen = fread_be(file, 4);	/* mvhd length */
43    fread(fourcc, sizeof(char), 4, file);
44    if(memcmp(fourcc, "mvhd", 4)!=0)
45       return;
46 
47    /* Now we're at the start of the movie header */
48 
49    /* 20: time scale (time units per second) (4 bytes) */
50    fseek(file, blockStart + 20, SEEK_SET);
51    timescale = fread_be(file, 4);
52 
53    /* 24: duration in time units (4 bytes) */
54    data->duration = (unsigned int) round_double((double) fread_be(file, 4)
55 						/ timescale * 1000);
56 
57    /* Skip the rest of the mvhd */
58    fseek(file, blockStart + blockLen, SEEK_SET);
59 
60    /* Find and parse trak atoms */
61    while(!feof(file))
62    {
63       unsigned int width, height;
64 
65       /* Find the next trak atom */
66       blockStart = ftell(file);
67       blockLen = fread_be(file, 4);	/* trak (or other atom) length */
68       fread(fourcc, sizeof(char), 4, file);
69       if(memcmp(fourcc, "trak", 4)!=0)	/* If it's not a trak atom, skip it */
70       {
71 	 if(!feof(file))
72 	    fseek(file, blockStart + blockLen, SEEK_SET);
73 	 continue;
74       }
75 
76       subBlockStart = ftell(file);
77       subBlockLen = fread_be(file, 4);	/* tkhd length */
78       fread(fourcc, sizeof(char), 4, file);
79       if(memcmp(fourcc, "tkhd", 4)!=0)
80 	 return;
81 
82       /* Now in the track header */
83 
84       /* 84: width (2 bytes) */
85       fseek(file, subBlockStart + 84, SEEK_SET);
86       width = fread_be(file, 2);
87 
88       /* 88: height (2 bytes) */
89       fseek(file, subBlockStart + 88, SEEK_SET);
90       height = fread_be(file, 2);
91 
92       /* Note on above: Apple's docs say that width/height are 4-byte integers,
93        * but all files I've seen have the data stored in the high-order two
94        * bytes, with the low-order two being 0x0000.  Interpreting it the
95        * "official" way would make width/height be thousands of pixels each.
96        */
97 
98       /* Skip rest of tkhd */
99       fseek(file, subBlockStart + subBlockLen, SEEK_SET);
100 
101       /* Find mdia atom for this trak */
102       subBlockStart = ftell(file);
103       subBlockLen = fread_be(file, 4);
104       fread(fourcc, sizeof(char), 4, file);
105       while(memcmp(fourcc, "mdia", 4)!=0)
106       {
107 	 fseek(file, subBlockStart + subBlockLen, SEEK_SET);
108 	 subBlockStart = ftell(file);
109 	 subBlockLen = fread_be(file, 4);
110 	 fread(fourcc, sizeof(char), 4, file);
111       }
112 
113       /* Now we're in the mdia atom; first sub-atom should be mdhd */
114       subSubBlockStart = ftell(file);
115       subSubBlockLen = fread_be(file, 4);
116       fread(fourcc, sizeof(char), 4, file);
117       if(memcmp(fourcc, "mdhd", 4)!=0)
118 	 return;
119       /* TODO: extract language from the mdhd?  For now skip to hdlr. */
120       fseek(file, subSubBlockStart + subSubBlockLen, SEEK_SET);
121       subSubBlockStart = ftell(file);
122       subSubBlockLen = fread_be(file, 4);
123       fread(fourcc, sizeof(char), 4, file);
124       if(memcmp(fourcc, "hdlr", 4)!=0)
125 	 return;
126       /* 12: Component type: "mhlr" or "dhlr"; we only care about mhlr,
127        * which should (?) appear first */
128       fseek(file, subSubBlockStart + 12, SEEK_SET);
129       fread(fourcc, sizeof(char), 4, file);
130       if(memcmp(fourcc, "mhlr", 4)!=0)
131 	 return;
132       fread(fourcc, sizeof(char), 4, file);
133       if(memcmp(fourcc, "vide", 4)==0)	/* This is a video trak */
134       {
135 	 data->height = height;
136 	 data->width = width;
137       }
138 
139       /* Skip rest of the trak */
140       fseek(file, blockStart + blockLen, SEEK_SET);
141    }
142 }
143