qt-faststart.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  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. fprintf(stderr, "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. fprintf(stderr, "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. fprintf(stderr, "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. fprintf(stderr, "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. fprintf(stderr, "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. fprintf(stderr, "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. fprintf(stderr, "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. fprintf(stderr, "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. fprintf(stderr, "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. fprintf(stderr, "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. uint64_t free_size = 0;
  378. uint64_t moov_size = 0;
  379. if (argc != 3) {
  380. printf("Usage: qt-faststart <infile.mov> <outfile.mov>\n"
  381. "Note: alternatively you can use -movflags +faststart in ffmpeg\n");
  382. return 0;
  383. }
  384. if (!strcmp(argv[1], argv[2])) {
  385. fprintf(stderr, "input and output files need to be different\n");
  386. return 1;
  387. }
  388. infile = fopen(argv[1], "rb");
  389. if (!infile) {
  390. perror(argv[1]);
  391. goto error_out;
  392. }
  393. /* traverse through the atoms in the file to make sure that 'moov' is
  394. * at the end */
  395. while (!feof(infile)) {
  396. if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
  397. break;
  398. }
  399. atom_size = BE_32(&atom_bytes[0]);
  400. atom_type = BE_32(&atom_bytes[4]);
  401. /* keep ftyp atom */
  402. if (atom_type == FTYP_ATOM) {
  403. if (atom_size > MAX_FTYP_ATOM_SIZE) {
  404. fprintf(stderr, "ftyp atom size %"PRIu64" too big\n",
  405. atom_size);
  406. goto error_out;
  407. }
  408. ftyp_atom_size = atom_size;
  409. free(ftyp_atom);
  410. ftyp_atom = malloc(ftyp_atom_size);
  411. if (!ftyp_atom) {
  412. fprintf(stderr, "could not allocate %"PRIu64" bytes for ftyp atom\n",
  413. atom_size);
  414. goto error_out;
  415. }
  416. if (fseeko(infile, -ATOM_PREAMBLE_SIZE, SEEK_CUR) ||
  417. fread(ftyp_atom, atom_size, 1, infile) != 1 ||
  418. (start_offset = ftello(infile)) < 0) {
  419. perror(argv[1]);
  420. goto error_out;
  421. }
  422. } else {
  423. int ret;
  424. /* 64-bit special case */
  425. if (atom_size == 1) {
  426. if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
  427. break;
  428. }
  429. atom_size = BE_64(&atom_bytes[0]);
  430. ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE * 2, SEEK_CUR);
  431. } else {
  432. ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR);
  433. }
  434. if (ret) {
  435. perror(argv[1]);
  436. goto error_out;
  437. }
  438. }
  439. printf("%c%c%c%c %10"PRIu64" %"PRIu64"\n",
  440. (atom_type >> 24) & 255,
  441. (atom_type >> 16) & 255,
  442. (atom_type >> 8) & 255,
  443. (atom_type >> 0) & 255,
  444. atom_offset,
  445. atom_size);
  446. if ((atom_type != FREE_ATOM) &&
  447. (atom_type != JUNK_ATOM) &&
  448. (atom_type != MDAT_ATOM) &&
  449. (atom_type != MOOV_ATOM) &&
  450. (atom_type != PNOT_ATOM) &&
  451. (atom_type != SKIP_ATOM) &&
  452. (atom_type != WIDE_ATOM) &&
  453. (atom_type != PICT_ATOM) &&
  454. (atom_type != UUID_ATOM) &&
  455. (atom_type != FTYP_ATOM)) {
  456. fprintf(stderr, "encountered non-QT top-level atom (is this a QuickTime file?)\n");
  457. break;
  458. }
  459. atom_offset += atom_size;
  460. /* The atom header is 8 (or 16 bytes), if the atom size (which
  461. * includes these 8 or 16 bytes) is less than that, we won't be
  462. * able to continue scanning sensibly after this atom, so break. */
  463. if (atom_size < 8)
  464. break;
  465. if (atom_type == MOOV_ATOM)
  466. moov_size = atom_size;
  467. if (moov_size && atom_type == FREE_ATOM) {
  468. free_size += atom_size;
  469. atom_type = MOOV_ATOM;
  470. atom_size = moov_size;
  471. }
  472. }
  473. if (atom_type != MOOV_ATOM) {
  474. printf("last atom in file was not a moov atom\n");
  475. free(ftyp_atom);
  476. fclose(infile);
  477. return 0;
  478. }
  479. if (atom_size < 16) {
  480. fprintf(stderr, "bad moov atom size\n");
  481. goto error_out;
  482. }
  483. /* moov atom was, in fact, the last atom in the chunk; load the whole
  484. * moov atom */
  485. if (fseeko(infile, -(atom_size + free_size), SEEK_END)) {
  486. perror(argv[1]);
  487. goto error_out;
  488. }
  489. last_offset = ftello(infile);
  490. if (last_offset < 0) {
  491. perror(argv[1]);
  492. goto error_out;
  493. }
  494. moov_atom_size = atom_size;
  495. moov_atom = malloc(moov_atom_size);
  496. if (!moov_atom) {
  497. fprintf(stderr, "could not allocate %"PRIu64" bytes for moov atom\n", atom_size);
  498. goto error_out;
  499. }
  500. if (fread(moov_atom, atom_size, 1, infile) != 1) {
  501. perror(argv[1]);
  502. goto error_out;
  503. }
  504. /* this utility does not support compressed atoms yet, so disqualify
  505. * files with compressed QT atoms */
  506. if (BE_32(&moov_atom[12]) == CMOV_ATOM) {
  507. fprintf(stderr, "this utility does not support compressed moov atoms yet\n");
  508. goto error_out;
  509. }
  510. /* close; will be re-opened later */
  511. fclose(infile);
  512. infile = NULL;
  513. if (update_moov_atom(&moov_atom, &moov_atom_size) < 0) {
  514. goto error_out;
  515. }
  516. /* re-open the input file and open the output file */
  517. infile = fopen(argv[1], "rb");
  518. if (!infile) {
  519. perror(argv[1]);
  520. goto error_out;
  521. }
  522. if (start_offset > 0) { /* seek after ftyp atom */
  523. if (fseeko(infile, start_offset, SEEK_SET)) {
  524. perror(argv[1]);
  525. goto error_out;
  526. }
  527. last_offset -= start_offset;
  528. }
  529. outfile = fopen(argv[2], "wb");
  530. if (!outfile) {
  531. perror(argv[2]);
  532. goto error_out;
  533. }
  534. /* dump the same ftyp atom */
  535. if (ftyp_atom_size > 0) {
  536. printf(" writing ftyp atom...\n");
  537. if (fwrite(ftyp_atom, ftyp_atom_size, 1, outfile) != 1) {
  538. perror(argv[2]);
  539. goto error_out;
  540. }
  541. }
  542. /* dump the new moov atom */
  543. printf(" writing moov atom...\n");
  544. if (fwrite(moov_atom, moov_atom_size, 1, outfile) != 1) {
  545. perror(argv[2]);
  546. goto error_out;
  547. }
  548. /* copy the remainder of the infile, from offset 0 -> last_offset - 1 */
  549. bytes_to_copy = MIN(COPY_BUFFER_SIZE, last_offset);
  550. copy_buffer = malloc(bytes_to_copy);
  551. if (!copy_buffer) {
  552. fprintf(stderr, "could not allocate %d bytes for copy_buffer\n", bytes_to_copy);
  553. goto error_out;
  554. }
  555. printf(" copying rest of file...\n");
  556. while (last_offset) {
  557. bytes_to_copy = MIN(bytes_to_copy, last_offset);
  558. if (fread(copy_buffer, bytes_to_copy, 1, infile) != 1) {
  559. perror(argv[1]);
  560. goto error_out;
  561. }
  562. if (fwrite(copy_buffer, bytes_to_copy, 1, outfile) != 1) {
  563. perror(argv[2]);
  564. goto error_out;
  565. }
  566. last_offset -= bytes_to_copy;
  567. }
  568. fclose(infile);
  569. fclose(outfile);
  570. free(moov_atom);
  571. free(ftyp_atom);
  572. free(copy_buffer);
  573. return 0;
  574. error_out:
  575. if (infile)
  576. fclose(infile);
  577. if (outfile)
  578. fclose(outfile);
  579. free(moov_atom);
  580. free(ftyp_atom);
  581. free(copy_buffer);
  582. return 1;
  583. }