]> git.sesse.net Git - ffmpeg/blob - tools/qt-faststart.c
avfilter/vf_psnr: remove unnecessary check
[ffmpeg] / 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 #include <limits.h>
32
33 #ifdef __MINGW32__
34 #undef fseeko
35 #define fseeko(x, y, z) fseeko64(x, y, z)
36 #undef ftello
37 #define ftello(x)       ftello64(x)
38 #elif defined(_WIN32)
39 #undef fseeko
40 #define fseeko(x, y, z) _fseeki64(x, y, z)
41 #undef ftello
42 #define ftello(x)       _ftelli64(x)
43 #endif
44
45 #define MIN(a,b) ((a) > (b) ? (b) : (a))
46
47 #define BE_32(x) (((uint32_t)(((uint8_t*)(x))[0]) << 24) |  \
48                              (((uint8_t*)(x))[1]  << 16) |  \
49                              (((uint8_t*)(x))[2]  <<  8) |  \
50                               ((uint8_t*)(x))[3])
51
52 #define BE_64(x) (((uint64_t)(((uint8_t*)(x))[0]) << 56) |  \
53                   ((uint64_t)(((uint8_t*)(x))[1]) << 48) |  \
54                   ((uint64_t)(((uint8_t*)(x))[2]) << 40) |  \
55                   ((uint64_t)(((uint8_t*)(x))[3]) << 32) |  \
56                   ((uint64_t)(((uint8_t*)(x))[4]) << 24) |  \
57                   ((uint64_t)(((uint8_t*)(x))[5]) << 16) |  \
58                   ((uint64_t)(((uint8_t*)(x))[6]) <<  8) |  \
59                   ((uint64_t)( (uint8_t*)(x))[7]))
60
61 #define AV_WB32(p, val)    {                    \
62     ((uint8_t*)(p))[0] = ((val) >> 24) & 0xff;  \
63     ((uint8_t*)(p))[1] = ((val) >> 16) & 0xff;  \
64     ((uint8_t*)(p))[2] = ((val) >> 8) & 0xff;   \
65     ((uint8_t*)(p))[3] = (val) & 0xff;          \
66     }
67
68 #define AV_WB64(p, val)    {                    \
69     AV_WB32(p, (val) >> 32)                     \
70     AV_WB32(p + 4, val)                         \
71     }
72
73 #define BE_FOURCC(ch0, ch1, ch2, ch3)           \
74     ( (uint32_t)(unsigned char)(ch3)        |   \
75      ((uint32_t)(unsigned char)(ch2) <<  8) |   \
76      ((uint32_t)(unsigned char)(ch1) << 16) |   \
77      ((uint32_t)(unsigned char)(ch0) << 24) )
78
79 #define QT_ATOM BE_FOURCC
80 /* top level atoms */
81 #define FREE_ATOM QT_ATOM('f', 'r', 'e', 'e')
82 #define JUNK_ATOM QT_ATOM('j', 'u', 'n', 'k')
83 #define MDAT_ATOM QT_ATOM('m', 'd', 'a', 't')
84 #define MOOV_ATOM QT_ATOM('m', 'o', 'o', 'v')
85 #define PNOT_ATOM QT_ATOM('p', 'n', 'o', 't')
86 #define SKIP_ATOM QT_ATOM('s', 'k', 'i', 'p')
87 #define WIDE_ATOM QT_ATOM('w', 'i', 'd', 'e')
88 #define PICT_ATOM QT_ATOM('P', 'I', 'C', 'T')
89 #define FTYP_ATOM QT_ATOM('f', 't', 'y', 'p')
90 #define UUID_ATOM QT_ATOM('u', 'u', 'i', 'd')
91
92 #define CMOV_ATOM QT_ATOM('c', 'm', 'o', 'v')
93 #define TRAK_ATOM QT_ATOM('t', 'r', 'a', 'k')
94 #define MDIA_ATOM QT_ATOM('m', 'd', 'i', 'a')
95 #define MINF_ATOM QT_ATOM('m', 'i', 'n', 'f')
96 #define STBL_ATOM QT_ATOM('s', 't', 'b', 'l')
97 #define STCO_ATOM QT_ATOM('s', 't', 'c', 'o')
98 #define CO64_ATOM QT_ATOM('c', 'o', '6', '4')
99
100 #define ATOM_PREAMBLE_SIZE    8
101 #define COPY_BUFFER_SIZE   33554432
102 #define MAX_FTYP_ATOM_SIZE 1048576
103
104 typedef struct {
105     uint32_t type;
106     uint32_t header_size;
107     uint64_t size;
108     unsigned char *data;
109 } atom_t;
110
111 typedef struct {
112     uint64_t moov_atom_size;
113     uint64_t stco_offset_count;
114     uint64_t stco_data_size;
115     int stco_overflow;
116     uint32_t depth;
117 } update_chunk_offsets_context_t;
118
119 typedef struct {
120     unsigned char *dest;
121     uint64_t original_moov_size;
122     uint64_t new_moov_size;
123 } upgrade_stco_context_t;
124
125 typedef int (*parse_atoms_callback_t)(void *context, atom_t *atom);
126
127 static int parse_atoms(
128     unsigned char *buf,
129     uint64_t size,
130     parse_atoms_callback_t callback,
131     void *context)
132 {
133     unsigned char *pos = buf;
134     unsigned char *end = pos + size;
135     atom_t atom;
136     int ret;
137
138     while (end - pos >= ATOM_PREAMBLE_SIZE) {
139         atom.size = BE_32(pos);
140         atom.type = BE_32(pos + 4);
141         pos += ATOM_PREAMBLE_SIZE;
142         atom.header_size = ATOM_PREAMBLE_SIZE;
143
144         switch (atom.size) {
145         case 1:
146             if (end - pos < 8) {
147                 fprintf(stderr, "not enough room for 64 bit atom size\n");
148                 return -1;
149             }
150
151             atom.size = BE_64(pos);
152             pos += 8;
153             atom.header_size = ATOM_PREAMBLE_SIZE + 8;
154             break;
155
156         case 0:
157             atom.size = ATOM_PREAMBLE_SIZE + end - pos;
158             break;
159         }
160
161         if (atom.size < atom.header_size) {
162             fprintf(stderr, "atom size %"PRIu64" too small\n", atom.size);
163             return -1;
164         }
165
166         atom.size -= atom.header_size;
167
168         if (atom.size > end - pos) {
169             fprintf(stderr, "atom size %"PRIu64" too big\n", atom.size);
170             return -1;
171         }
172
173         atom.data = pos;
174         ret = callback(context, &atom);
175         if (ret < 0) {
176             return ret;
177         }
178
179         pos += atom.size;
180     }
181
182     return 0;
183 }
184
185 static int update_stco_offsets(update_chunk_offsets_context_t *context, atom_t *atom)
186 {
187     uint32_t current_offset;
188     uint32_t offset_count;
189     unsigned char *pos;
190     unsigned char *end;
191
192     printf(" patching stco atom...\n");
193     if (atom->size < 8) {
194         fprintf(stderr, "stco atom size %"PRIu64" too small\n", atom->size);
195         return -1;
196     }
197
198     offset_count = BE_32(atom->data + 4);
199     if (offset_count > (atom->size - 8) / 4) {
200         fprintf(stderr, "stco offset count %"PRIu32" too big\n", offset_count);
201         return -1;
202     }
203
204     context->stco_offset_count += offset_count;
205     context->stco_data_size += atom->size - 8;
206
207     for (pos = atom->data + 8, end = pos + offset_count * 4;
208         pos < end;
209         pos += 4) {
210         current_offset = BE_32(pos);
211         if (current_offset > UINT_MAX - context->moov_atom_size) {
212             context->stco_overflow = 1;
213         }
214         current_offset += context->moov_atom_size;
215         AV_WB32(pos, current_offset);
216     }
217
218     return 0;
219 }
220
221 static int update_co64_offsets(update_chunk_offsets_context_t *context, atom_t *atom)
222 {
223     uint64_t current_offset;
224     uint32_t offset_count;
225     unsigned char *pos;
226     unsigned char *end;
227
228     printf(" patching co64 atom...\n");
229     if (atom->size < 8) {
230         fprintf(stderr, "co64 atom size %"PRIu64" too small\n", atom->size);
231         return -1;
232     }
233
234     offset_count = BE_32(atom->data + 4);
235     if (offset_count > (atom->size - 8) / 8) {
236         fprintf(stderr, "co64 offset count %"PRIu32" too big\n", offset_count);
237         return -1;
238     }
239
240     for (pos = atom->data + 8, end = pos + offset_count * 8;
241         pos < end;
242         pos += 8) {
243         current_offset = BE_64(pos);
244         current_offset += context->moov_atom_size;
245         AV_WB64(pos, current_offset);
246     }
247
248     return 0;
249 }
250
251 static int update_chunk_offsets_callback(void *ctx, atom_t *atom)
252 {
253     update_chunk_offsets_context_t *context = ctx;
254     int ret;
255
256     switch (atom->type) {
257     case STCO_ATOM:
258         return update_stco_offsets(context, atom);
259
260     case CO64_ATOM:
261         return update_co64_offsets(context, atom);
262
263     case MOOV_ATOM:
264     case TRAK_ATOM:
265     case MDIA_ATOM:
266     case MINF_ATOM:
267     case STBL_ATOM:
268         context->depth++;
269         if (context->depth > 10) {
270             fprintf(stderr, "atoms too deeply nested\n");
271             return -1;
272         }
273
274         ret = parse_atoms(
275             atom->data,
276             atom->size,
277             update_chunk_offsets_callback,
278             context);
279         context->depth--;
280         return ret;
281     }
282
283     return 0;
284 }
285
286 static void set_atom_size(unsigned char *header, uint32_t header_size, uint64_t size)
287 {
288     switch (header_size) {
289     case 8:
290         AV_WB32(header, size);
291         break;
292
293     case 16:
294         AV_WB64(header + 8, size);
295         break;
296     }
297 }
298
299 static void upgrade_stco_atom(upgrade_stco_context_t *context, atom_t *atom)
300 {
301     unsigned char *pos;
302     unsigned char *end;
303     uint64_t new_offset;
304     uint32_t offset_count;
305     uint32_t original_offset;
306
307     /* Note: not performing validations since they were performed on the first pass */
308
309     offset_count = BE_32(atom->data + 4);
310
311     /* write the header */
312     memcpy(context->dest, atom->data - atom->header_size, atom->header_size + 8);
313     AV_WB32(context->dest + 4, CO64_ATOM);
314     set_atom_size(context->dest, atom->header_size, atom->header_size + 8 + offset_count * 8);
315     context->dest += atom->header_size + 8;
316
317     /* write the data */
318     for (pos = atom->data + 8, end = pos + offset_count * 4;
319         pos < end;
320         pos += 4) {
321         original_offset = BE_32(pos) - context->original_moov_size;
322         new_offset = (uint64_t)original_offset + context->new_moov_size;
323         AV_WB64(context->dest, new_offset);
324         context->dest += 8;
325     }
326 }
327
328 static int upgrade_stco_callback(void *ctx, atom_t *atom)
329 {
330     upgrade_stco_context_t *context = ctx;
331     unsigned char *start_pos;
332     uint64_t copy_size;
333
334     switch (atom->type) {
335     case STCO_ATOM:
336         upgrade_stco_atom(context, atom);
337         break;
338
339     case MOOV_ATOM:
340     case TRAK_ATOM:
341     case MDIA_ATOM:
342     case MINF_ATOM:
343     case STBL_ATOM:
344         /* write the atom header */
345         memcpy(context->dest, atom->data - atom->header_size, atom->header_size);
346         start_pos = context->dest;
347         context->dest += atom->header_size;
348
349         /* parse internal atoms*/
350         if (parse_atoms(
351             atom->data,
352             atom->size,
353             upgrade_stco_callback,
354             context) < 0) {
355             return -1;
356         }
357
358         /* update the atom size */
359         set_atom_size(start_pos, atom->header_size, context->dest - start_pos);
360         break;
361
362     default:
363         copy_size = atom->header_size + atom->size;
364         memcpy(context->dest, atom->data - atom->header_size, copy_size);
365         context->dest += copy_size;
366         break;
367     }
368
369     return 0;
370 }
371
372 static int update_moov_atom(
373     unsigned char **moov_atom,
374     uint64_t *moov_atom_size)
375 {
376     update_chunk_offsets_context_t update_context = { 0 };
377     upgrade_stco_context_t upgrade_context;
378     unsigned char *new_moov_atom;
379
380     update_context.moov_atom_size = *moov_atom_size;
381
382     if (parse_atoms(
383         *moov_atom,
384         *moov_atom_size,
385         update_chunk_offsets_callback,
386         &update_context) < 0) {
387         return -1;
388     }
389
390     if (!update_context.stco_overflow) {
391         return 0;
392     }
393
394     printf(" upgrading stco atoms to co64...\n");
395     upgrade_context.new_moov_size = *moov_atom_size +
396         update_context.stco_offset_count * 8 -
397         update_context.stco_data_size;
398
399     new_moov_atom = malloc(upgrade_context.new_moov_size);
400     if (new_moov_atom == NULL) {
401         fprintf(stderr, "could not allocate %"PRIu64" bytes for updated moov atom\n",
402             upgrade_context.new_moov_size);
403         return -1;
404     }
405
406     upgrade_context.original_moov_size = *moov_atom_size;
407     upgrade_context.dest = new_moov_atom;
408
409     if (parse_atoms(
410         *moov_atom,
411         *moov_atom_size,
412         upgrade_stco_callback,
413         &upgrade_context) < 0) {
414         free(new_moov_atom);
415         return -1;
416     }
417
418     free(*moov_atom);
419     *moov_atom = new_moov_atom;
420     *moov_atom_size = upgrade_context.new_moov_size;
421
422     if (upgrade_context.dest != *moov_atom + *moov_atom_size) {
423         fprintf(stderr, "unexpected - wrong number of moov bytes written\n");
424         return -1;
425     }
426
427     return 0;
428 }
429
430 int main(int argc, char *argv[])
431 {
432     FILE *infile  = NULL;
433     FILE *outfile = NULL;
434     unsigned char atom_bytes[ATOM_PREAMBLE_SIZE];
435     uint32_t atom_type   = 0;
436     uint64_t atom_size   = 0;
437     uint64_t atom_offset = 0;
438     int64_t last_offset;
439     unsigned char *moov_atom = NULL;
440     unsigned char *ftyp_atom = NULL;
441     uint64_t moov_atom_size;
442     uint64_t ftyp_atom_size = 0;
443     int64_t start_offset = 0;
444     unsigned char *copy_buffer = NULL;
445     int bytes_to_copy;
446     uint64_t free_size = 0;
447     uint64_t moov_size = 0;
448
449     if (argc != 3) {
450         printf("Usage: qt-faststart <infile.mov> <outfile.mov>\n"
451                "Note: alternatively you can use -movflags +faststart in ffmpeg\n");
452         return 0;
453     }
454
455     if (!strcmp(argv[1], argv[2])) {
456         fprintf(stderr, "input and output files need to be different\n");
457         return 1;
458     }
459
460     infile = fopen(argv[1], "rb");
461     if (!infile) {
462         perror(argv[1]);
463         goto error_out;
464     }
465
466     /* traverse through the atoms in the file to make sure that 'moov' is
467      * at the end */
468     while (!feof(infile)) {
469         if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
470             break;
471         }
472         atom_size = BE_32(&atom_bytes[0]);
473         atom_type = BE_32(&atom_bytes[4]);
474
475         /* keep ftyp atom */
476         if (atom_type == FTYP_ATOM) {
477             if (atom_size > MAX_FTYP_ATOM_SIZE) {
478                 fprintf(stderr, "ftyp atom size %"PRIu64" too big\n",
479                        atom_size);
480                 goto error_out;
481             }
482             ftyp_atom_size = atom_size;
483             free(ftyp_atom);
484             ftyp_atom = malloc(ftyp_atom_size);
485             if (!ftyp_atom) {
486                 fprintf(stderr, "could not allocate %"PRIu64" bytes for ftyp atom\n",
487                        atom_size);
488                 goto error_out;
489             }
490             if (fseeko(infile, -ATOM_PREAMBLE_SIZE, SEEK_CUR) ||
491                 fread(ftyp_atom, atom_size, 1, infile) != 1 ||
492                 (start_offset = ftello(infile)) < 0) {
493                 perror(argv[1]);
494                 goto error_out;
495             }
496         } else {
497             int ret;
498             /* 64-bit special case */
499             if (atom_size == 1) {
500                 if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
501                     break;
502                 }
503                 atom_size = BE_64(&atom_bytes[0]);
504                 ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE * 2, SEEK_CUR);
505             } else {
506                 ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR);
507             }
508             if (ret) {
509                 perror(argv[1]);
510                 goto error_out;
511             }
512         }
513         printf("%c%c%c%c %10"PRIu64" %"PRIu64"\n",
514                (atom_type >> 24) & 255,
515                (atom_type >> 16) & 255,
516                (atom_type >>  8) & 255,
517                (atom_type >>  0) & 255,
518                atom_offset,
519                atom_size);
520         if ((atom_type != FREE_ATOM) &&
521             (atom_type != JUNK_ATOM) &&
522             (atom_type != MDAT_ATOM) &&
523             (atom_type != MOOV_ATOM) &&
524             (atom_type != PNOT_ATOM) &&
525             (atom_type != SKIP_ATOM) &&
526             (atom_type != WIDE_ATOM) &&
527             (atom_type != PICT_ATOM) &&
528             (atom_type != UUID_ATOM) &&
529             (atom_type != FTYP_ATOM)) {
530             fprintf(stderr, "encountered non-QT top-level atom (is this a QuickTime file?)\n");
531             break;
532         }
533         atom_offset += atom_size;
534
535         /* The atom header is 8 (or 16 bytes), if the atom size (which
536          * includes these 8 or 16 bytes) is less than that, we won't be
537          * able to continue scanning sensibly after this atom, so break. */
538         if (atom_size < 8)
539             break;
540
541         if (atom_type == MOOV_ATOM)
542             moov_size = atom_size;
543
544         if (moov_size && atom_type == FREE_ATOM) {
545             free_size += atom_size;
546             atom_type = MOOV_ATOM;
547             atom_size = moov_size;
548         }
549     }
550
551     if (atom_type != MOOV_ATOM) {
552         printf("last atom in file was not a moov atom\n");
553         free(ftyp_atom);
554         fclose(infile);
555         return 0;
556     }
557
558     if (atom_size < 16) {
559         fprintf(stderr, "bad moov atom size\n");
560         goto error_out;
561     }
562
563     /* moov atom was, in fact, the last atom in the chunk; load the whole
564      * moov atom */
565     if (fseeko(infile, -(atom_size + free_size), SEEK_END)) {
566         perror(argv[1]);
567         goto error_out;
568     }
569     last_offset    = ftello(infile);
570     if (last_offset < 0) {
571         perror(argv[1]);
572         goto error_out;
573     }
574     moov_atom_size = atom_size;
575     moov_atom      = malloc(moov_atom_size);
576     if (!moov_atom) {
577         fprintf(stderr, "could not allocate %"PRIu64" bytes for moov atom\n", atom_size);
578         goto error_out;
579     }
580     if (fread(moov_atom, atom_size, 1, infile) != 1) {
581         perror(argv[1]);
582         goto error_out;
583     }
584
585     /* this utility does not support compressed atoms yet, so disqualify
586      * files with compressed QT atoms */
587     if (BE_32(&moov_atom[12]) == CMOV_ATOM) {
588         fprintf(stderr, "this utility does not support compressed moov atoms yet\n");
589         goto error_out;
590     }
591
592     /* close; will be re-opened later */
593     fclose(infile);
594     infile = NULL;
595
596     if (update_moov_atom(&moov_atom, &moov_atom_size) < 0) {
597         goto error_out;
598     }
599
600     /* re-open the input file and open the output file */
601     infile = fopen(argv[1], "rb");
602     if (!infile) {
603         perror(argv[1]);
604         goto error_out;
605     }
606
607     if (start_offset > 0) { /* seek after ftyp atom */
608         if (fseeko(infile, start_offset, SEEK_SET)) {
609             perror(argv[1]);
610             goto error_out;
611         }
612
613         last_offset -= start_offset;
614     }
615
616     outfile = fopen(argv[2], "wb");
617     if (!outfile) {
618         perror(argv[2]);
619         goto error_out;
620     }
621
622     /* dump the same ftyp atom */
623     if (ftyp_atom_size > 0) {
624         printf(" writing ftyp atom...\n");
625         if (fwrite(ftyp_atom, ftyp_atom_size, 1, outfile) != 1) {
626             perror(argv[2]);
627             goto error_out;
628         }
629     }
630
631     /* dump the new moov atom */
632     printf(" writing moov atom...\n");
633     if (fwrite(moov_atom, moov_atom_size, 1, outfile) != 1) {
634         perror(argv[2]);
635         goto error_out;
636     }
637
638     /* copy the remainder of the infile, from offset 0 -> last_offset - 1 */
639     bytes_to_copy = MIN(COPY_BUFFER_SIZE, last_offset);
640     copy_buffer = malloc(bytes_to_copy);
641     if (!copy_buffer) {
642         fprintf(stderr, "could not allocate %d bytes for copy_buffer\n", bytes_to_copy);
643         goto error_out;
644     }
645     printf(" copying rest of file...\n");
646     while (last_offset) {
647         bytes_to_copy = MIN(bytes_to_copy, last_offset);
648
649         if (fread(copy_buffer, bytes_to_copy, 1, infile) != 1) {
650             perror(argv[1]);
651             goto error_out;
652         }
653         if (fwrite(copy_buffer, bytes_to_copy, 1, outfile) != 1) {
654             perror(argv[2]);
655             goto error_out;
656         }
657         last_offset -= bytes_to_copy;
658     }
659
660     fclose(infile);
661     fclose(outfile);
662     free(moov_atom);
663     free(ftyp_atom);
664     free(copy_buffer);
665
666     return 0;
667
668 error_out:
669     if (infile)
670         fclose(infile);
671     if (outfile)
672         fclose(outfile);
673     free(moov_atom);
674     free(ftyp_atom);
675     free(copy_buffer);
676     return 1;
677 }