qt-faststart.c
Go to the documentation of this file.
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 __MINGW32__
33 #define fseeko(x, y, z) fseeko64(x, y, z)
34 #define ftello(x) ftello64(x)
35 #elif defined(_WIN32)
36 #define fseeko(x, y, z) _fseeki64(x, y, z)
37 #define ftello(x) _ftelli64(x)
38 #endif
39 
40 #define FFMIN(a,b) ((a) > (b) ? (b) : (a))
41 
42 #define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])
43 
44 #define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \
45  (((uint8_t*)(x))[1] << 16) | \
46  (((uint8_t*)(x))[2] << 8) | \
47  ((uint8_t*)(x))[3])
48 
49 #define BE_64(x) (((uint64_t)(((uint8_t*)(x))[0]) << 56) | \
50  ((uint64_t)(((uint8_t*)(x))[1]) << 48) | \
51  ((uint64_t)(((uint8_t*)(x))[2]) << 40) | \
52  ((uint64_t)(((uint8_t*)(x))[3]) << 32) | \
53  ((uint64_t)(((uint8_t*)(x))[4]) << 24) | \
54  ((uint64_t)(((uint8_t*)(x))[5]) << 16) | \
55  ((uint64_t)(((uint8_t*)(x))[6]) << 8) | \
56  ((uint64_t)( (uint8_t*)(x))[7]))
57 
58 #define BE_FOURCC(ch0, ch1, ch2, ch3) \
59  ( (uint32_t)(unsigned char)(ch3) | \
60  ((uint32_t)(unsigned char)(ch2) << 8) | \
61  ((uint32_t)(unsigned char)(ch1) << 16) | \
62  ((uint32_t)(unsigned char)(ch0) << 24) )
63 
64 #define QT_ATOM BE_FOURCC
65 /* top level atoms */
66 #define FREE_ATOM QT_ATOM('f', 'r', 'e', 'e')
67 #define JUNK_ATOM QT_ATOM('j', 'u', 'n', 'k')
68 #define MDAT_ATOM QT_ATOM('m', 'd', 'a', 't')
69 #define MOOV_ATOM QT_ATOM('m', 'o', 'o', 'v')
70 #define PNOT_ATOM QT_ATOM('p', 'n', 'o', 't')
71 #define SKIP_ATOM QT_ATOM('s', 'k', 'i', 'p')
72 #define WIDE_ATOM QT_ATOM('w', 'i', 'd', 'e')
73 #define PICT_ATOM QT_ATOM('P', 'I', 'C', 'T')
74 #define FTYP_ATOM QT_ATOM('f', 't', 'y', 'p')
75 #define UUID_ATOM QT_ATOM('u', 'u', 'i', 'd')
76 
77 #define CMOV_ATOM QT_ATOM('c', 'm', 'o', 'v')
78 #define STCO_ATOM QT_ATOM('s', 't', 'c', 'o')
79 #define CO64_ATOM QT_ATOM('c', 'o', '6', '4')
80 
81 #define ATOM_PREAMBLE_SIZE 8
82 #define COPY_BUFFER_SIZE 33554432
83 
84 int main(int argc, char *argv[])
85 {
86  FILE *infile = NULL;
87  FILE *outfile = NULL;
88  unsigned char atom_bytes[ATOM_PREAMBLE_SIZE];
89  uint32_t atom_type = 0;
90  uint64_t atom_size = 0;
91  uint64_t atom_offset = 0;
92  uint64_t last_offset;
93  unsigned char *moov_atom = NULL;
94  unsigned char *ftyp_atom = NULL;
95  uint64_t moov_atom_size;
96  uint64_t ftyp_atom_size = 0;
97  uint64_t i, j;
98  uint32_t offset_count;
99  uint64_t current_offset;
100  int64_t start_offset = 0;
101  unsigned char *copy_buffer = NULL;
102  int bytes_to_copy;
103 
104  if (argc != 3) {
105  printf("Usage: qt-faststart <infile.mov> <outfile.mov>\n");
106  return 0;
107  }
108 
109  if (!strcmp(argv[1], argv[2])) {
110  fprintf(stderr, "input and output files need to be different\n");
111  return 1;
112  }
113 
114  infile = fopen(argv[1], "rb");
115  if (!infile) {
116  perror(argv[1]);
117  goto error_out;
118  }
119 
120  /* traverse through the atoms in the file to make sure that 'moov' is
121  * at the end */
122  while (!feof(infile)) {
123  if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
124  break;
125  }
126  atom_size = (uint32_t) BE_32(&atom_bytes[0]);
127  atom_type = BE_32(&atom_bytes[4]);
128 
129  /* keep ftyp atom */
130  if (atom_type == FTYP_ATOM) {
131  ftyp_atom_size = atom_size;
132  free(ftyp_atom);
133  ftyp_atom = malloc(ftyp_atom_size);
134  if (!ftyp_atom) {
135  printf("could not allocate %"PRIu64" bytes for ftyp atom\n",
136  atom_size);
137  goto error_out;
138  }
139  if ( fseeko(infile, -ATOM_PREAMBLE_SIZE, SEEK_CUR)
140  || fread(ftyp_atom, atom_size, 1, infile) != 1
141  || (start_offset = ftello(infile))<0) {
142  perror(argv[1]);
143  goto error_out;
144  }
145  } else {
146  int ret;
147  /* 64-bit special case */
148  if (atom_size == 1) {
149  if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
150  break;
151  }
152  atom_size = BE_64(&atom_bytes[0]);
153  ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE * 2, SEEK_CUR);
154  } else {
155  ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR);
156  }
157  if(ret) {
158  perror(argv[1]);
159  goto error_out;
160  }
161  }
162  printf("%c%c%c%c %10"PRIu64" %"PRIu64"\n",
163  (atom_type >> 24) & 255,
164  (atom_type >> 16) & 255,
165  (atom_type >> 8) & 255,
166  (atom_type >> 0) & 255,
167  atom_offset,
168  atom_size);
169  if ((atom_type != FREE_ATOM) &&
170  (atom_type != JUNK_ATOM) &&
171  (atom_type != MDAT_ATOM) &&
172  (atom_type != MOOV_ATOM) &&
173  (atom_type != PNOT_ATOM) &&
174  (atom_type != SKIP_ATOM) &&
175  (atom_type != WIDE_ATOM) &&
176  (atom_type != PICT_ATOM) &&
177  (atom_type != UUID_ATOM) &&
178  (atom_type != FTYP_ATOM)) {
179  printf("encountered non-QT top-level atom (is this a QuickTime file?)\n");
180  break;
181  }
182  atom_offset += atom_size;
183 
184  /* The atom header is 8 (or 16 bytes), if the atom size (which
185  * includes these 8 or 16 bytes) is less than that, we won't be
186  * able to continue scanning sensibly after this atom, so break. */
187  if (atom_size < 8)
188  break;
189  }
190 
191  if (atom_type != MOOV_ATOM) {
192  printf("last atom in file was not a moov atom\n");
193  free(ftyp_atom);
194  fclose(infile);
195  return 0;
196  }
197 
198  /* moov atom was, in fact, the last atom in the chunk; load the whole
199  * moov atom */
200  if (fseeko(infile, -atom_size, SEEK_END)) {
201  perror(argv[1]);
202  goto error_out;
203  }
204  last_offset = ftello(infile);
205  moov_atom_size = atom_size;
206  moov_atom = malloc(moov_atom_size);
207  if (!moov_atom) {
208  printf("could not allocate %"PRIu64" bytes for moov atom\n", atom_size);
209  goto error_out;
210  }
211  if (fread(moov_atom, atom_size, 1, infile) != 1) {
212  perror(argv[1]);
213  goto error_out;
214  }
215 
216  /* this utility does not support compressed atoms yet, so disqualify
217  * files with compressed QT atoms */
218  if (BE_32(&moov_atom[12]) == CMOV_ATOM) {
219  printf("this utility does not support compressed moov atoms yet\n");
220  goto error_out;
221  }
222 
223  /* close; will be re-opened later */
224  fclose(infile);
225  infile = NULL;
226 
227  /* crawl through the moov chunk in search of stco or co64 atoms */
228  for (i = 4; i < moov_atom_size - 4; i++) {
229  atom_type = BE_32(&moov_atom[i]);
230  if (atom_type == STCO_ATOM) {
231  printf(" patching stco atom...\n");
232  atom_size = BE_32(&moov_atom[i - 4]);
233  if (i + atom_size - 4 > moov_atom_size) {
234  printf(" bad atom size\n");
235  goto error_out;
236  }
237  offset_count = BE_32(&moov_atom[i + 8]);
238  if (i + 12LL + offset_count * 4LL > moov_atom_size) {
239  printf(" bad atom size\n");
240  goto error_out;
241  }
242  for (j = 0; j < offset_count; j++) {
243  current_offset = BE_32(&moov_atom[i + 12 + j * 4]);
244  current_offset += moov_atom_size;
245  moov_atom[i + 12 + j * 4 + 0] = (current_offset >> 24) & 0xFF;
246  moov_atom[i + 12 + j * 4 + 1] = (current_offset >> 16) & 0xFF;
247  moov_atom[i + 12 + j * 4 + 2] = (current_offset >> 8) & 0xFF;
248  moov_atom[i + 12 + j * 4 + 3] = (current_offset >> 0) & 0xFF;
249  }
250  i += atom_size - 4;
251  } else if (atom_type == CO64_ATOM) {
252  printf(" patching co64 atom...\n");
253  atom_size = BE_32(&moov_atom[i - 4]);
254  if (i + atom_size - 4 > moov_atom_size) {
255  printf(" bad atom size\n");
256  goto error_out;
257  }
258  offset_count = BE_32(&moov_atom[i + 8]);
259  if (i + 12LL + offset_count * 8LL > moov_atom_size) {
260  printf(" bad atom size\n");
261  goto error_out;
262  }
263  for (j = 0; j < offset_count; j++) {
264  current_offset = BE_64(&moov_atom[i + 12 + j * 8]);
265  current_offset += moov_atom_size;
266  moov_atom[i + 12 + j * 8 + 0] = (current_offset >> 56) & 0xFF;
267  moov_atom[i + 12 + j * 8 + 1] = (current_offset >> 48) & 0xFF;
268  moov_atom[i + 12 + j * 8 + 2] = (current_offset >> 40) & 0xFF;
269  moov_atom[i + 12 + j * 8 + 3] = (current_offset >> 32) & 0xFF;
270  moov_atom[i + 12 + j * 8 + 4] = (current_offset >> 24) & 0xFF;
271  moov_atom[i + 12 + j * 8 + 5] = (current_offset >> 16) & 0xFF;
272  moov_atom[i + 12 + j * 8 + 6] = (current_offset >> 8) & 0xFF;
273  moov_atom[i + 12 + j * 8 + 7] = (current_offset >> 0) & 0xFF;
274  }
275  i += atom_size - 4;
276  }
277  }
278 
279  /* re-open the input file and open the output file */
280  infile = fopen(argv[1], "rb");
281  if (!infile) {
282  perror(argv[1]);
283  goto error_out;
284  }
285 
286  if (start_offset > 0) { /* seek after ftyp atom */
287  if (fseeko(infile, start_offset, SEEK_SET)) {
288  perror(argv[1]);
289  goto error_out;
290  }
291 
292  last_offset -= start_offset;
293  }
294 
295  outfile = fopen(argv[2], "wb");
296  if (!outfile) {
297  perror(argv[2]);
298  goto error_out;
299  }
300 
301  /* dump the same ftyp atom */
302  if (ftyp_atom_size > 0) {
303  printf(" writing ftyp atom...\n");
304  if (fwrite(ftyp_atom, ftyp_atom_size, 1, outfile) != 1) {
305  perror(argv[2]);
306  goto error_out;
307  }
308  }
309 
310  /* dump the new moov atom */
311  printf(" writing moov atom...\n");
312  if (fwrite(moov_atom, moov_atom_size, 1, outfile) != 1) {
313  perror(argv[2]);
314  goto error_out;
315  }
316 
317  /* copy the remainder of the infile, from offset 0 -> last_offset - 1 */
318  bytes_to_copy = FFMIN(COPY_BUFFER_SIZE, last_offset);
319  copy_buffer = malloc(bytes_to_copy);
320  if (!copy_buffer) {
321  printf("could not allocate %d bytes for copy_buffer\n", bytes_to_copy);
322  goto error_out;
323  }
324  printf(" copying rest of file...\n");
325  while (last_offset) {
326  bytes_to_copy = FFMIN(bytes_to_copy, last_offset);
327 
328  if (fread(copy_buffer, bytes_to_copy, 1, infile) != 1) {
329  perror(argv[1]);
330  goto error_out;
331  }
332  if (fwrite(copy_buffer, bytes_to_copy, 1, outfile) != 1) {
333  perror(argv[2]);
334  goto error_out;
335  }
336  last_offset -= bytes_to_copy;
337  }
338 
339  fclose(infile);
340  fclose(outfile);
341  free(moov_atom);
342  free(ftyp_atom);
343  free(copy_buffer);
344 
345  return 0;
346 
347 error_out:
348  if (infile)
349  fclose(infile);
350  if (outfile)
351  fclose(outfile);
352  free(moov_atom);
353  free(ftyp_atom);
354  free(copy_buffer);
355  return 1;
356 }
#define MOOV_ATOM
Definition: qt-faststart.c:69
#define BE_32(x)
Definition: qt-faststart.c:44
#define COPY_BUFFER_SIZE
Definition: qt-faststart.c:82
#define FREE_ATOM
Definition: qt-faststart.c:66
#define PNOT_ATOM
Definition: qt-faststart.c:70
#define ATOM_PREAMBLE_SIZE
Definition: qt-faststart.c:81
#define JUNK_ATOM
Definition: qt-faststart.c:67
#define CMOV_ATOM
Definition: qt-faststart.c:77
#define MDAT_ATOM
Definition: qt-faststart.c:68
#define CO64_ATOM
Definition: qt-faststart.c:79
#define UUID_ATOM
Definition: qt-faststart.c:75
ret
Definition: avfilter.c:821
NULL
Definition: eval.c:55
Close file fclose(fid)
#define STCO_ATOM
Definition: qt-faststart.c:78
#define WIDE_ATOM
Definition: qt-faststart.c:72
synthesis window for stochastic i
#define BE_64(x)
Definition: qt-faststart.c:49
#define FTYP_ATOM
Definition: qt-faststart.c:74
#define FFMIN(a, b)
Definition: qt-faststart.c:40
int main(int argc, char *argv[])
Definition: qt-faststart.c:84
printf("static const uint8_t my_array[100] = {\n")
#define SKIP_ATOM
Definition: qt-faststart.c:71
#define PICT_ATOM
Definition: qt-faststart.c:73
FILE * outfile
Definition: audiogen.c:96
MUSIC TECHNOLOGY GROUP UNIVERSITAT POMPEU FABRA Free Non Commercial Binary License Agreement UNIVERSITAT POMPEU OR INDICATING ACCEPTANCE BY SELECTING THE ACCEPT BUTTON ON ANY DOWNLOAD OR INSTALL YOU ACCEPT THE TERMS OF THE LICENSE SUMMARY TABLE Software MELODIA Melody Extraction vamp plug in Licensor Music Technology Group Universitat Pompeu Plaça de la Spain Permitted purposes Non commercial internal research and validation and educational purposes only All commercial uses in a production either internal or are prohibited by this license and require an additional commercial exploitation license TERMS AND CONDITIONS SOFTWARE Software means the software programs identified herein in binary any other machine readable any updates or error corrections provided by and any user programming guides and other documentation provided to you by UPF under this Agreement LICENSE Subject to the terms and conditions of this UPF grants you a royalty free