OSDN Git Service

Merge commit 'd08e02d929ff8be5f56bb1da0e439bf1ae557552'
[android-x86/external-ffmpeg.git] / tools / qt-faststart.c
1 /*
2  * qt-faststart.c, v0.2
3  * by Mike Melanson (melanson@pcisys.net)
4  * This file is placed in the public domain. Use the program however you
5  * see fit.
6  *
7  * This utility rearranges a Quicktime file such that the moov atom
8  * is in front of the data, thus facilitating network streaming.
9  *
10  * To compile this program, start from the base directory from which you
11  * are building FFmpeg and type:
12  *  make tools/qt-faststart
13  * The qt-faststart program will be built in the tools/ directory. If you
14  * do not build the program in this manner, correct results are not
15  * guaranteed, particularly on 64-bit platforms.
16  * Invoke the program with:
17  *  qt-faststart <infile.mov> <outfile.mov>
18  *
19  * Notes: Quicktime files can come in many configurations of top-level
20  * atoms. This utility stipulates that the very last atom in the file needs
21  * to be a moov atom. When given such a file, this utility will rearrange
22  * the top-level atoms by shifting the moov atom from the back of the file
23  * to the front, and patch the chunk offsets along the way. This utility
24  * presently only operates on uncompressed moov atoms.
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <inttypes.h>
30 #include <string.h>
31
32 #ifdef __MINGW32CE__
33 #define fseeko(x, y, z) fseek(x, y, z)
34 #define ftello(x)       ftell(x)
35 #elif defined(__MINGW32__)
36 #undef fseeko
37 #define fseeko(x, y, z) fseeko64(x, y, z)
38 #undef ftello
39 #define ftello(x)       ftello64(x)
40 #elif defined(_WIN32)
41 #undef fseeko
42 #define fseeko(x, y, z) _fseeki64(x, y, z)
43 #undef ftello
44 #define ftello(x)       _ftelli64(x)
45 #endif
46
47 #define MIN(a,b) ((a) > (b) ? (b) : (a))
48
49 #define BE_16(x) ((((uint8_t*)(x))[0] <<  8) | ((uint8_t*)(x))[1])
50
51 #define BE_32(x) (((uint32_t)(((uint8_t*)(x))[0]) << 24) |  \
52                              (((uint8_t*)(x))[1]  << 16) |  \
53                              (((uint8_t*)(x))[2]  <<  8) |  \
54                               ((uint8_t*)(x))[3])
55
56 #define BE_64(x) (((uint64_t)(((uint8_t*)(x))[0]) << 56) |  \
57                   ((uint64_t)(((uint8_t*)(x))[1]) << 48) |  \
58                   ((uint64_t)(((uint8_t*)(x))[2]) << 40) |  \
59                   ((uint64_t)(((uint8_t*)(x))[3]) << 32) |  \
60                   ((uint64_t)(((uint8_t*)(x))[4]) << 24) |  \
61                   ((uint64_t)(((uint8_t*)(x))[5]) << 16) |  \
62                   ((uint64_t)(((uint8_t*)(x))[6]) <<  8) |  \
63                   ((uint64_t)( (uint8_t*)(x))[7]))
64
65 #define BE_FOURCC(ch0, ch1, ch2, ch3)           \
66     ( (uint32_t)(unsigned char)(ch3)        |   \
67      ((uint32_t)(unsigned char)(ch2) <<  8) |   \
68      ((uint32_t)(unsigned char)(ch1) << 16) |   \
69      ((uint32_t)(unsigned char)(ch0) << 24) )
70
71 #define QT_ATOM BE_FOURCC
72 /* top level atoms */
73 #define FREE_ATOM QT_ATOM('f', 'r', 'e', 'e')
74 #define JUNK_ATOM QT_ATOM('j', 'u', 'n', 'k')
75 #define MDAT_ATOM QT_ATOM('m', 'd', 'a', 't')
76 #define MOOV_ATOM QT_ATOM('m', 'o', 'o', 'v')
77 #define PNOT_ATOM QT_ATOM('p', 'n', 'o', 't')
78 #define SKIP_ATOM QT_ATOM('s', 'k', 'i', 'p')
79 #define WIDE_ATOM QT_ATOM('w', 'i', 'd', 'e')
80 #define PICT_ATOM QT_ATOM('P', 'I', 'C', 'T')
81 #define FTYP_ATOM QT_ATOM('f', 't', 'y', 'p')
82 #define UUID_ATOM QT_ATOM('u', 'u', 'i', 'd')
83
84 #define CMOV_ATOM QT_ATOM('c', 'm', 'o', 'v')
85 #define STCO_ATOM QT_ATOM('s', 't', 'c', 'o')
86 #define CO64_ATOM QT_ATOM('c', 'o', '6', '4')
87
88 #define ATOM_PREAMBLE_SIZE    8
89 #define COPY_BUFFER_SIZE   33554432
90
91 int main(int argc, char *argv[])
92 {
93     FILE *infile  = NULL;
94     FILE *outfile = NULL;
95     unsigned char atom_bytes[ATOM_PREAMBLE_SIZE];
96     uint32_t atom_type   = 0;
97     uint64_t atom_size   = 0;
98     uint64_t atom_offset = 0;
99     int64_t last_offset;
100     unsigned char *moov_atom = NULL;
101     unsigned char *ftyp_atom = NULL;
102     uint64_t moov_atom_size;
103     uint64_t ftyp_atom_size = 0;
104     uint64_t i, j;
105     uint32_t offset_count;
106     uint64_t current_offset;
107     int64_t start_offset = 0;
108     unsigned char *copy_buffer = NULL;
109     int bytes_to_copy;
110
111     if (argc != 3) {
112         printf("Usage: qt-faststart <infile.mov> <outfile.mov>\n"
113                "Note: alternatively you can use -movflags +faststart in ffmpeg\n");
114         return 0;
115     }
116
117     if (!strcmp(argv[1], argv[2])) {
118         fprintf(stderr, "input and output files need to be different\n");
119         return 1;
120     }
121
122     infile = fopen(argv[1], "rb");
123     if (!infile) {
124         perror(argv[1]);
125         goto error_out;
126     }
127
128     /* traverse through the atoms in the file to make sure that 'moov' is
129      * at the end */
130     while (!feof(infile)) {
131         if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
132             break;
133         }
134         atom_size = BE_32(&atom_bytes[0]);
135         atom_type = BE_32(&atom_bytes[4]);
136
137         /* keep ftyp atom */
138         if (atom_type == FTYP_ATOM) {
139             ftyp_atom_size = atom_size;
140             free(ftyp_atom);
141             ftyp_atom = malloc(ftyp_atom_size);
142             if (!ftyp_atom) {
143                 printf("could not allocate %"PRIu64" bytes for ftyp atom\n",
144                        atom_size);
145                 goto error_out;
146             }
147             if (fseeko(infile, -ATOM_PREAMBLE_SIZE, SEEK_CUR) ||
148                 fread(ftyp_atom, atom_size, 1, infile) != 1 ||
149                 (start_offset = ftello(infile)) < 0) {
150                 perror(argv[1]);
151                 goto error_out;
152             }
153         } else {
154             int ret;
155             /* 64-bit special case */
156             if (atom_size == 1) {
157                 if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
158                     break;
159                 }
160                 atom_size = BE_64(&atom_bytes[0]);
161                 ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE * 2, SEEK_CUR);
162             } else {
163                 ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR);
164             }
165             if (ret) {
166                 perror(argv[1]);
167                 goto error_out;
168             }
169         }
170         printf("%c%c%c%c %10"PRIu64" %"PRIu64"\n",
171                (atom_type >> 24) & 255,
172                (atom_type >> 16) & 255,
173                (atom_type >>  8) & 255,
174                (atom_type >>  0) & 255,
175                atom_offset,
176                atom_size);
177         if ((atom_type != FREE_ATOM) &&
178             (atom_type != JUNK_ATOM) &&
179             (atom_type != MDAT_ATOM) &&
180             (atom_type != MOOV_ATOM) &&
181             (atom_type != PNOT_ATOM) &&
182             (atom_type != SKIP_ATOM) &&
183             (atom_type != WIDE_ATOM) &&
184             (atom_type != PICT_ATOM) &&
185             (atom_type != UUID_ATOM) &&
186             (atom_type != FTYP_ATOM)) {
187             printf("encountered non-QT top-level atom (is this a QuickTime file?)\n");
188             break;
189         }
190         atom_offset += atom_size;
191
192         /* The atom header is 8 (or 16 bytes), if the atom size (which
193          * includes these 8 or 16 bytes) is less than that, we won't be
194          * able to continue scanning sensibly after this atom, so break. */
195         if (atom_size < 8)
196             break;
197     }
198
199     if (atom_type != MOOV_ATOM) {
200         printf("last atom in file was not a moov atom\n");
201         free(ftyp_atom);
202         fclose(infile);
203         return 0;
204     }
205
206     /* moov atom was, in fact, the last atom in the chunk; load the whole
207      * moov atom */
208     if (fseeko(infile, -atom_size, SEEK_END)) {
209         perror(argv[1]);
210         goto error_out;
211     }
212     last_offset    = ftello(infile);
213     if (last_offset < 0) {
214         perror(argv[1]);
215         goto error_out;
216     }
217     moov_atom_size = atom_size;
218     moov_atom      = malloc(moov_atom_size);
219     if (!moov_atom) {
220         printf("could not allocate %"PRIu64" bytes for moov atom\n", atom_size);
221         goto error_out;
222     }
223     if (fread(moov_atom, atom_size, 1, infile) != 1) {
224         perror(argv[1]);
225         goto error_out;
226     }
227
228     /* this utility does not support compressed atoms yet, so disqualify
229      * files with compressed QT atoms */
230     if (BE_32(&moov_atom[12]) == CMOV_ATOM) {
231         printf("this utility does not support compressed moov atoms yet\n");
232         goto error_out;
233     }
234
235     /* close; will be re-opened later */
236     fclose(infile);
237     infile = NULL;
238
239     /* crawl through the moov chunk in search of stco or co64 atoms */
240     for (i = 4; i < moov_atom_size - 4; i++) {
241         atom_type = BE_32(&moov_atom[i]);
242         if (atom_type == STCO_ATOM) {
243             printf(" patching stco atom...\n");
244             atom_size = BE_32(&moov_atom[i - 4]);
245             if (i + atom_size - 4 > moov_atom_size) {
246                 printf(" bad atom size\n");
247                 goto error_out;
248             }
249             offset_count = BE_32(&moov_atom[i + 8]);
250             if (i + 12 + offset_count * UINT64_C(4) > moov_atom_size) {
251                 printf(" bad atom size/element count\n");
252                 goto error_out;
253             }
254             for (j = 0; j < offset_count; j++) {
255                 current_offset  = BE_32(&moov_atom[i + 12 + j * 4]);
256                 current_offset += moov_atom_size;
257                 moov_atom[i + 12 + j * 4 + 0] = (current_offset >> 24) & 0xFF;
258                 moov_atom[i + 12 + j * 4 + 1] = (current_offset >> 16) & 0xFF;
259                 moov_atom[i + 12 + j * 4 + 2] = (current_offset >>  8) & 0xFF;
260                 moov_atom[i + 12 + j * 4 + 3] = (current_offset >>  0) & 0xFF;
261             }
262             i += atom_size - 4;
263         } else if (atom_type == CO64_ATOM) {
264             printf(" patching co64 atom...\n");
265             atom_size = BE_32(&moov_atom[i - 4]);
266             if (i + atom_size - 4 > moov_atom_size) {
267                 printf(" bad atom size\n");
268                 goto error_out;
269             }
270             offset_count = BE_32(&moov_atom[i + 8]);
271             if (i + 12 + offset_count * UINT64_C(8) > moov_atom_size) {
272                 printf(" bad atom size/element count\n");
273                 goto error_out;
274             }
275             for (j = 0; j < offset_count; j++) {
276                 current_offset  = BE_64(&moov_atom[i + 12 + j * 8]);
277                 current_offset += moov_atom_size;
278                 moov_atom[i + 12 + j * 8 + 0] = (current_offset >> 56) & 0xFF;
279                 moov_atom[i + 12 + j * 8 + 1] = (current_offset >> 48) & 0xFF;
280                 moov_atom[i + 12 + j * 8 + 2] = (current_offset >> 40) & 0xFF;
281                 moov_atom[i + 12 + j * 8 + 3] = (current_offset >> 32) & 0xFF;
282                 moov_atom[i + 12 + j * 8 + 4] = (current_offset >> 24) & 0xFF;
283                 moov_atom[i + 12 + j * 8 + 5] = (current_offset >> 16) & 0xFF;
284                 moov_atom[i + 12 + j * 8 + 6] = (current_offset >>  8) & 0xFF;
285                 moov_atom[i + 12 + j * 8 + 7] = (current_offset >>  0) & 0xFF;
286             }
287             i += atom_size - 4;
288         }
289     }
290
291     /* re-open the input file and open the output file */
292     infile = fopen(argv[1], "rb");
293     if (!infile) {
294         perror(argv[1]);
295         goto error_out;
296     }
297
298     if (start_offset > 0) { /* seek after ftyp atom */
299         if (fseeko(infile, start_offset, SEEK_SET)) {
300             perror(argv[1]);
301             goto error_out;
302         }
303
304         last_offset -= start_offset;
305     }
306
307     outfile = fopen(argv[2], "wb");
308     if (!outfile) {
309         perror(argv[2]);
310         goto error_out;
311     }
312
313     /* dump the same ftyp atom */
314     if (ftyp_atom_size > 0) {
315         printf(" writing ftyp atom...\n");
316         if (fwrite(ftyp_atom, ftyp_atom_size, 1, outfile) != 1) {
317             perror(argv[2]);
318             goto error_out;
319         }
320     }
321
322     /* dump the new moov atom */
323     printf(" writing moov atom...\n");
324     if (fwrite(moov_atom, moov_atom_size, 1, outfile) != 1) {
325         perror(argv[2]);
326         goto error_out;
327     }
328
329     /* copy the remainder of the infile, from offset 0 -> last_offset - 1 */
330     bytes_to_copy = MIN(COPY_BUFFER_SIZE, last_offset);
331     copy_buffer = malloc(bytes_to_copy);
332     if (!copy_buffer) {
333         printf("could not allocate %d bytes for copy_buffer\n", bytes_to_copy);
334         goto error_out;
335     }
336     printf(" copying rest of file...\n");
337     while (last_offset) {
338         bytes_to_copy = MIN(bytes_to_copy, last_offset);
339
340         if (fread(copy_buffer, bytes_to_copy, 1, infile) != 1) {
341             perror(argv[1]);
342             goto error_out;
343         }
344         if (fwrite(copy_buffer, bytes_to_copy, 1, outfile) != 1) {
345             perror(argv[2]);
346             goto error_out;
347         }
348         last_offset -= bytes_to_copy;
349     }
350
351     fclose(infile);
352     fclose(outfile);
353     free(moov_atom);
354     free(ftyp_atom);
355     free(copy_buffer);
356
357     return 0;
358
359 error_out:
360     if (infile)
361         fclose(infile);
362     if (outfile)
363         fclose(outfile);
364     free(moov_atom);
365     free(ftyp_atom);
366     free(copy_buffer);
367     return 1;
368 }