Line | Branch | Exec | Source |
---|---|---|---|
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 | 12 | static int parse_atoms( | |
128 | unsigned char *buf, | ||
129 | uint64_t size, | ||
130 | parse_atoms_callback_t callback, | ||
131 | void *context) | ||
132 | { | ||
133 | 12 | unsigned char *pos = buf; | |
134 | 12 | unsigned char *end = pos + size; | |
135 | atom_t atom; | ||
136 | int ret; | ||
137 | |||
138 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 12 times.
|
50 | while (end - pos >= ATOM_PREAMBLE_SIZE) { |
139 | 38 | atom.size = BE_32(pos); | |
140 | 38 | atom.type = BE_32(pos + 4); | |
141 | 38 | pos += ATOM_PREAMBLE_SIZE; | |
142 | 38 | atom.header_size = ATOM_PREAMBLE_SIZE; | |
143 | |||
144 |
1/3✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
|
38 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
|
38 | if (atom.size < atom.header_size) { |
162 | ✗ | fprintf(stderr, "atom size %"PRIu64" too small\n", atom.size); | |
163 | ✗ | return -1; | |
164 | } | ||
165 | |||
166 | 38 | atom.size -= atom.header_size; | |
167 | |||
168 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
|
38 | if (atom.size > end - pos) { |
169 | ✗ | fprintf(stderr, "atom size %"PRIu64" too big\n", atom.size); | |
170 | ✗ | return -1; | |
171 | } | ||
172 | |||
173 | 38 | atom.data = pos; | |
174 | 38 | ret = callback(context, &atom); | |
175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
|
38 | if (ret < 0) { |
176 | ✗ | return ret; | |
177 | } | ||
178 | |||
179 | 38 | pos += atom.size; | |
180 | } | ||
181 | |||
182 | 12 | return 0; | |
183 | } | ||
184 | |||
185 | 1 | 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 | 1 | printf(" patching stco atom...\n"); | |
193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (atom->size < 8) { |
194 | ✗ | fprintf(stderr, "stco atom size %"PRIu64" too small\n", atom->size); | |
195 | ✗ | return -1; | |
196 | } | ||
197 | |||
198 | 1 | offset_count = BE_32(atom->data + 4); | |
199 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 | 1 | context->stco_offset_count += offset_count; | |
205 | 1 | context->stco_data_size += atom->size - 8; | |
206 | |||
207 | 1 | for (pos = atom->data + 8, end = pos + offset_count * 4; | |
208 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | pos < end; |
209 | 1 | pos += 4) { | |
210 | 1 | current_offset = BE_32(pos); | |
211 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (current_offset > UINT_MAX - context->moov_atom_size) { |
212 | 1 | context->stco_overflow = 1; | |
213 | } | ||
214 | 1 | current_offset += context->moov_atom_size; | |
215 | 1 | AV_WB32(pos, current_offset); | |
216 | } | ||
217 | |||
218 | 1 | 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 | 19 | static int update_chunk_offsets_callback(void *ctx, atom_t *atom) | |
252 | { | ||
253 | 19 | update_chunk_offsets_context_t *context = ctx; | |
254 | int ret; | ||
255 | |||
256 |
3/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 13 times.
|
19 | switch (atom->type) { |
257 | 1 | case STCO_ATOM: | |
258 | 1 | return update_stco_offsets(context, atom); | |
259 | |||
260 | ✗ | case CO64_ATOM: | |
261 | ✗ | return update_co64_offsets(context, atom); | |
262 | |||
263 | 5 | case MOOV_ATOM: | |
264 | case TRAK_ATOM: | ||
265 | case MDIA_ATOM: | ||
266 | case MINF_ATOM: | ||
267 | case STBL_ATOM: | ||
268 | 5 | context->depth++; | |
269 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (context->depth > 10) { |
270 | ✗ | fprintf(stderr, "atoms too deeply nested\n"); | |
271 | ✗ | return -1; | |
272 | } | ||
273 | |||
274 | 5 | ret = parse_atoms( | |
275 | atom->data, | ||
276 | atom->size, | ||
277 | update_chunk_offsets_callback, | ||
278 | context); | ||
279 | 5 | context->depth--; | |
280 | 5 | return ret; | |
281 | } | ||
282 | |||
283 | 13 | return 0; | |
284 | } | ||
285 | |||
286 | 6 | static void set_atom_size(unsigned char *header, uint32_t header_size, uint64_t size) | |
287 | { | ||
288 |
1/3✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
6 | switch (header_size) { |
289 | 6 | case 8: | |
290 | 6 | AV_WB32(header, size); | |
291 | 6 | break; | |
292 | |||
293 | ✗ | case 16: | |
294 | ✗ | AV_WB64(header + 8, size); | |
295 | ✗ | break; | |
296 | } | ||
297 | 6 | } | |
298 | |||
299 | 1 | 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 | 1 | offset_count = BE_32(atom->data + 4); | |
310 | |||
311 | /* write the header */ | ||
312 | 1 | memcpy(context->dest, atom->data - atom->header_size, atom->header_size + 8); | |
313 | 1 | AV_WB32(context->dest + 4, CO64_ATOM); | |
314 | 1 | set_atom_size(context->dest, atom->header_size, atom->header_size + 8 + offset_count * 8); | |
315 | 1 | context->dest += atom->header_size + 8; | |
316 | |||
317 | /* write the data */ | ||
318 | 1 | for (pos = atom->data + 8, end = pos + offset_count * 4; | |
319 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | pos < end; |
320 | 1 | pos += 4) { | |
321 | 1 | original_offset = BE_32(pos) - context->original_moov_size; | |
322 | 1 | new_offset = (uint64_t)original_offset + context->new_moov_size; | |
323 | 1 | AV_WB64(context->dest, new_offset); | |
324 | 1 | context->dest += 8; | |
325 | } | ||
326 | 1 | } | |
327 | |||
328 | 19 | static int upgrade_stco_callback(void *ctx, atom_t *atom) | |
329 | { | ||
330 | 19 | upgrade_stco_context_t *context = ctx; | |
331 | unsigned char *start_pos; | ||
332 | uint64_t copy_size; | ||
333 | |||
334 |
3/3✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 13 times.
|
19 | switch (atom->type) { |
335 | 1 | case STCO_ATOM: | |
336 | 1 | upgrade_stco_atom(context, atom); | |
337 | 1 | break; | |
338 | |||
339 | 5 | 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 | 5 | memcpy(context->dest, atom->data - atom->header_size, atom->header_size); | |
346 | 5 | start_pos = context->dest; | |
347 | 5 | context->dest += atom->header_size; | |
348 | |||
349 | /* parse internal atoms*/ | ||
350 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | 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 | 5 | set_atom_size(start_pos, atom->header_size, context->dest - start_pos); | |
360 | 5 | break; | |
361 | |||
362 | 13 | default: | |
363 | 13 | copy_size = atom->header_size + atom->size; | |
364 | 13 | memcpy(context->dest, atom->data - atom->header_size, copy_size); | |
365 | 13 | context->dest += copy_size; | |
366 | 13 | break; | |
367 | } | ||
368 | |||
369 | 19 | return 0; | |
370 | } | ||
371 | |||
372 | 1 | static int update_moov_atom( | |
373 | unsigned char **moov_atom, | ||
374 | uint64_t *moov_atom_size) | ||
375 | { | ||
376 | 1 | update_chunk_offsets_context_t update_context = { 0 }; | |
377 | upgrade_stco_context_t upgrade_context; | ||
378 | unsigned char *new_moov_atom; | ||
379 | |||
380 | 1 | update_context.moov_atom_size = *moov_atom_size; | |
381 | |||
382 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!update_context.stco_overflow) { |
391 | ✗ | return 0; | |
392 | } | ||
393 | |||
394 | 1 | printf(" upgrading stco atoms to co64...\n"); | |
395 | 1 | upgrade_context.new_moov_size = *moov_atom_size + | |
396 | 1 | update_context.stco_offset_count * 8 - | |
397 | 1 | update_context.stco_data_size; | |
398 | |||
399 | 1 | new_moov_atom = malloc(upgrade_context.new_moov_size); | |
400 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 | 1 | upgrade_context.original_moov_size = *moov_atom_size; | |
407 | 1 | upgrade_context.dest = new_moov_atom; | |
408 | |||
409 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | 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 | 1 | free(*moov_atom); | |
419 | 1 | *moov_atom = new_moov_atom; | |
420 | 1 | *moov_atom_size = upgrade_context.new_moov_size; | |
421 | |||
422 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 | 1 | return 0; | |
428 | } | ||
429 | |||
430 | 1 | int main(int argc, char *argv[]) | |
431 | { | ||
432 | 1 | FILE *infile = NULL; | |
433 | 1 | FILE *outfile = NULL; | |
434 | unsigned char atom_bytes[ATOM_PREAMBLE_SIZE]; | ||
435 | 1 | uint32_t atom_type = 0; | |
436 | 1 | uint64_t atom_size = 0; | |
437 | 1 | uint64_t atom_offset = 0; | |
438 | int64_t last_offset; | ||
439 | 1 | unsigned char *moov_atom = NULL; | |
440 | 1 | unsigned char *ftyp_atom = NULL; | |
441 | uint64_t moov_atom_size; | ||
442 | 1 | uint64_t ftyp_atom_size = 0; | |
443 | 1 | int64_t start_offset = 0; | |
444 | 1 | unsigned char *copy_buffer = NULL; | |
445 | int bytes_to_copy; | ||
446 | 1 | uint64_t free_size = 0; | |
447 | 1 | uint64_t moov_size = 0; | |
448 | |||
449 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!strcmp(argv[1], argv[2])) { |
456 | ✗ | fprintf(stderr, "input and output files need to be different\n"); | |
457 | ✗ | return 1; | |
458 | } | ||
459 | |||
460 | 1 | infile = fopen(argv[1], "rb"); | |
461 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | while (!feof(infile)) { |
469 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
|
5 | if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) { |
470 | 1 | break; | |
471 | } | ||
472 | 4 | atom_size = BE_32(&atom_bytes[0]); | |
473 | 4 | atom_type = BE_32(&atom_bytes[4]); | |
474 | |||
475 | /* keep ftyp atom */ | ||
476 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if (atom_type == FTYP_ATOM) { |
477 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 | 1 | ftyp_atom_size = atom_size; | |
483 | 1 | free(ftyp_atom); | |
484 | 1 | ftyp_atom = malloc(ftyp_atom_size); | |
485 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!ftyp_atom) { |
486 | ✗ | fprintf(stderr, "could not allocate %"PRIu64" bytes for ftyp atom\n", | |
487 | atom_size); | ||
488 | ✗ | goto error_out; | |
489 | } | ||
490 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
2 | if (fseeko(infile, -ATOM_PREAMBLE_SIZE, SEEK_CUR) || |
491 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
2 | fread(ftyp_atom, atom_size, 1, infile) != 1 || |
492 | 1 | (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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | 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 | 3 | ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR); | |
507 | } | ||
508 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ret) { |
509 | ✗ | perror(argv[1]); | |
510 | ✗ | goto error_out; | |
511 | } | ||
512 | } | ||
513 | 4 | printf("%c%c%c%c %10"PRIu64" %"PRIu64"\n", | |
514 | (atom_type >> 24) & 255, | ||
515 | 4 | (atom_type >> 16) & 255, | |
516 | 4 | (atom_type >> 8) & 255, | |
517 | (atom_type >> 0) & 255, | ||
518 | atom_offset, | ||
519 | atom_size); | ||
520 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | if ((atom_type != FREE_ATOM) && |
521 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | (atom_type != JUNK_ATOM) && |
522 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | (atom_type != MDAT_ATOM) && |
523 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | (atom_type != MOOV_ATOM) && |
524 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | (atom_type != PNOT_ATOM) && |
525 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | (atom_type != SKIP_ATOM) && |
526 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | (atom_type != WIDE_ATOM) && |
527 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | (atom_type != PICT_ATOM) && |
528 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | (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 | 4 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (atom_size < 8) |
539 | ✗ | break; | |
540 | |||
541 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if (atom_type == MOOV_ATOM) |
542 | 1 | moov_size = atom_size; | |
543 | |||
544 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
4 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (fseeko(infile, -(atom_size + free_size), SEEK_END)) { |
566 | ✗ | perror(argv[1]); | |
567 | ✗ | goto error_out; | |
568 | } | ||
569 | 1 | last_offset = ftello(infile); | |
570 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (last_offset < 0) { |
571 | ✗ | perror(argv[1]); | |
572 | ✗ | goto error_out; | |
573 | } | ||
574 | 1 | moov_atom_size = atom_size; | |
575 | 1 | moov_atom = malloc(moov_atom_size); | |
576 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!moov_atom) { |
577 | ✗ | fprintf(stderr, "could not allocate %"PRIu64" bytes for moov atom\n", atom_size); | |
578 | ✗ | goto error_out; | |
579 | } | ||
580 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | 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 | 1 | fclose(infile); | |
594 | 1 | infile = NULL; | |
595 | |||
596 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | 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 | 1 | infile = fopen(argv[1], "rb"); | |
602 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!infile) { |
603 | ✗ | perror(argv[1]); | |
604 | ✗ | goto error_out; | |
605 | } | ||
606 | |||
607 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (start_offset > 0) { /* seek after ftyp atom */ |
608 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (fseeko(infile, start_offset, SEEK_SET)) { |
609 | ✗ | perror(argv[1]); | |
610 | ✗ | goto error_out; | |
611 | } | ||
612 | |||
613 | 1 | last_offset -= start_offset; | |
614 | } | ||
615 | |||
616 | 1 | outfile = fopen(argv[2], "wb"); | |
617 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!outfile) { |
618 | ✗ | perror(argv[2]); | |
619 | ✗ | goto error_out; | |
620 | } | ||
621 | |||
622 | /* dump the same ftyp atom */ | ||
623 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (ftyp_atom_size > 0) { |
624 | 1 | printf(" writing ftyp atom...\n"); | |
625 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | 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 | 1 | printf(" writing moov atom...\n"); | |
633 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | 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 | 1 | bytes_to_copy = MIN(COPY_BUFFER_SIZE, last_offset); | |
640 | 1 | copy_buffer = malloc(bytes_to_copy); | |
641 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!copy_buffer) { |
642 | ✗ | fprintf(stderr, "could not allocate %d bytes for copy_buffer\n", bytes_to_copy); | |
643 | ✗ | goto error_out; | |
644 | } | ||
645 | 1 | printf(" copying rest of file...\n"); | |
646 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | while (last_offset) { |
647 | 1 | bytes_to_copy = MIN(bytes_to_copy, last_offset); | |
648 | |||
649 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (fread(copy_buffer, bytes_to_copy, 1, infile) != 1) { |
650 | ✗ | perror(argv[1]); | |
651 | ✗ | goto error_out; | |
652 | } | ||
653 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (fwrite(copy_buffer, bytes_to_copy, 1, outfile) != 1) { |
654 | ✗ | perror(argv[2]); | |
655 | ✗ | goto error_out; | |
656 | } | ||
657 | 1 | last_offset -= bytes_to_copy; | |
658 | } | ||
659 | |||
660 | 1 | fclose(infile); | |
661 | 1 | fclose(outfile); | |
662 | 1 | free(moov_atom); | |
663 | 1 | free(ftyp_atom); | |
664 | 1 | free(copy_buffer); | |
665 | |||
666 | 1 | 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 | } | ||
678 |