qt-faststart.c 19 KB

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