annotate ffmpeg/tools/qt-faststart.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents f445c3017523
children
rev   line source
yading@11 1 /*
yading@11 2 * qt-faststart.c, v0.2
yading@11 3 * by Mike Melanson (melanson@pcisys.net)
yading@11 4 * This file is placed in the public domain. Use the program however you
yading@11 5 * see fit.
yading@11 6 *
yading@11 7 * This utility rearranges a Quicktime file such that the moov atom
yading@11 8 * is in front of the data, thus facilitating network streaming.
yading@11 9 *
yading@11 10 * To compile this program, start from the base directory from which you
yading@11 11 * are building FFmpeg and type:
yading@11 12 * make tools/qt-faststart
yading@11 13 * The qt-faststart program will be built in the tools/ directory. If you
yading@11 14 * do not build the program in this manner, correct results are not
yading@11 15 * guaranteed, particularly on 64-bit platforms.
yading@11 16 * Invoke the program with:
yading@11 17 * qt-faststart <infile.mov> <outfile.mov>
yading@11 18 *
yading@11 19 * Notes: Quicktime files can come in many configurations of top-level
yading@11 20 * atoms. This utility stipulates that the very last atom in the file needs
yading@11 21 * to be a moov atom. When given such a file, this utility will rearrange
yading@11 22 * the top-level atoms by shifting the moov atom from the back of the file
yading@11 23 * to the front, and patch the chunk offsets along the way. This utility
yading@11 24 * presently only operates on uncompressed moov atoms.
yading@11 25 */
yading@11 26
yading@11 27 #include <stdio.h>
yading@11 28 #include <stdlib.h>
yading@11 29 #include <inttypes.h>
yading@11 30 #include <string.h>
yading@11 31
yading@11 32 #ifdef __MINGW32__
yading@11 33 #define fseeko(x, y, z) fseeko64(x, y, z)
yading@11 34 #define ftello(x) ftello64(x)
yading@11 35 #elif defined(_WIN32)
yading@11 36 #define fseeko(x, y, z) _fseeki64(x, y, z)
yading@11 37 #define ftello(x) _ftelli64(x)
yading@11 38 #endif
yading@11 39
yading@11 40 #define FFMIN(a,b) ((a) > (b) ? (b) : (a))
yading@11 41
yading@11 42 #define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])
yading@11 43
yading@11 44 #define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \
yading@11 45 (((uint8_t*)(x))[1] << 16) | \
yading@11 46 (((uint8_t*)(x))[2] << 8) | \
yading@11 47 ((uint8_t*)(x))[3])
yading@11 48
yading@11 49 #define BE_64(x) (((uint64_t)(((uint8_t*)(x))[0]) << 56) | \
yading@11 50 ((uint64_t)(((uint8_t*)(x))[1]) << 48) | \
yading@11 51 ((uint64_t)(((uint8_t*)(x))[2]) << 40) | \
yading@11 52 ((uint64_t)(((uint8_t*)(x))[3]) << 32) | \
yading@11 53 ((uint64_t)(((uint8_t*)(x))[4]) << 24) | \
yading@11 54 ((uint64_t)(((uint8_t*)(x))[5]) << 16) | \
yading@11 55 ((uint64_t)(((uint8_t*)(x))[6]) << 8) | \
yading@11 56 ((uint64_t)( (uint8_t*)(x))[7]))
yading@11 57
yading@11 58 #define BE_FOURCC(ch0, ch1, ch2, ch3) \
yading@11 59 ( (uint32_t)(unsigned char)(ch3) | \
yading@11 60 ((uint32_t)(unsigned char)(ch2) << 8) | \
yading@11 61 ((uint32_t)(unsigned char)(ch1) << 16) | \
yading@11 62 ((uint32_t)(unsigned char)(ch0) << 24) )
yading@11 63
yading@11 64 #define QT_ATOM BE_FOURCC
yading@11 65 /* top level atoms */
yading@11 66 #define FREE_ATOM QT_ATOM('f', 'r', 'e', 'e')
yading@11 67 #define JUNK_ATOM QT_ATOM('j', 'u', 'n', 'k')
yading@11 68 #define MDAT_ATOM QT_ATOM('m', 'd', 'a', 't')
yading@11 69 #define MOOV_ATOM QT_ATOM('m', 'o', 'o', 'v')
yading@11 70 #define PNOT_ATOM QT_ATOM('p', 'n', 'o', 't')
yading@11 71 #define SKIP_ATOM QT_ATOM('s', 'k', 'i', 'p')
yading@11 72 #define WIDE_ATOM QT_ATOM('w', 'i', 'd', 'e')
yading@11 73 #define PICT_ATOM QT_ATOM('P', 'I', 'C', 'T')
yading@11 74 #define FTYP_ATOM QT_ATOM('f', 't', 'y', 'p')
yading@11 75 #define UUID_ATOM QT_ATOM('u', 'u', 'i', 'd')
yading@11 76
yading@11 77 #define CMOV_ATOM QT_ATOM('c', 'm', 'o', 'v')
yading@11 78 #define STCO_ATOM QT_ATOM('s', 't', 'c', 'o')
yading@11 79 #define CO64_ATOM QT_ATOM('c', 'o', '6', '4')
yading@11 80
yading@11 81 #define ATOM_PREAMBLE_SIZE 8
yading@11 82 #define COPY_BUFFER_SIZE 33554432
yading@11 83
yading@11 84 int main(int argc, char *argv[])
yading@11 85 {
yading@11 86 FILE *infile = NULL;
yading@11 87 FILE *outfile = NULL;
yading@11 88 unsigned char atom_bytes[ATOM_PREAMBLE_SIZE];
yading@11 89 uint32_t atom_type = 0;
yading@11 90 uint64_t atom_size = 0;
yading@11 91 uint64_t atom_offset = 0;
yading@11 92 uint64_t last_offset;
yading@11 93 unsigned char *moov_atom = NULL;
yading@11 94 unsigned char *ftyp_atom = NULL;
yading@11 95 uint64_t moov_atom_size;
yading@11 96 uint64_t ftyp_atom_size = 0;
yading@11 97 uint64_t i, j;
yading@11 98 uint32_t offset_count;
yading@11 99 uint64_t current_offset;
yading@11 100 int64_t start_offset = 0;
yading@11 101 unsigned char *copy_buffer = NULL;
yading@11 102 int bytes_to_copy;
yading@11 103
yading@11 104 if (argc != 3) {
yading@11 105 printf("Usage: qt-faststart <infile.mov> <outfile.mov>\n");
yading@11 106 return 0;
yading@11 107 }
yading@11 108
yading@11 109 if (!strcmp(argv[1], argv[2])) {
yading@11 110 fprintf(stderr, "input and output files need to be different\n");
yading@11 111 return 1;
yading@11 112 }
yading@11 113
yading@11 114 infile = fopen(argv[1], "rb");
yading@11 115 if (!infile) {
yading@11 116 perror(argv[1]);
yading@11 117 goto error_out;
yading@11 118 }
yading@11 119
yading@11 120 /* traverse through the atoms in the file to make sure that 'moov' is
yading@11 121 * at the end */
yading@11 122 while (!feof(infile)) {
yading@11 123 if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
yading@11 124 break;
yading@11 125 }
yading@11 126 atom_size = (uint32_t) BE_32(&atom_bytes[0]);
yading@11 127 atom_type = BE_32(&atom_bytes[4]);
yading@11 128
yading@11 129 /* keep ftyp atom */
yading@11 130 if (atom_type == FTYP_ATOM) {
yading@11 131 ftyp_atom_size = atom_size;
yading@11 132 free(ftyp_atom);
yading@11 133 ftyp_atom = malloc(ftyp_atom_size);
yading@11 134 if (!ftyp_atom) {
yading@11 135 printf("could not allocate %"PRIu64" bytes for ftyp atom\n",
yading@11 136 atom_size);
yading@11 137 goto error_out;
yading@11 138 }
yading@11 139 if ( fseeko(infile, -ATOM_PREAMBLE_SIZE, SEEK_CUR)
yading@11 140 || fread(ftyp_atom, atom_size, 1, infile) != 1
yading@11 141 || (start_offset = ftello(infile))<0) {
yading@11 142 perror(argv[1]);
yading@11 143 goto error_out;
yading@11 144 }
yading@11 145 } else {
yading@11 146 int ret;
yading@11 147 /* 64-bit special case */
yading@11 148 if (atom_size == 1) {
yading@11 149 if (fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile) != 1) {
yading@11 150 break;
yading@11 151 }
yading@11 152 atom_size = BE_64(&atom_bytes[0]);
yading@11 153 ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE * 2, SEEK_CUR);
yading@11 154 } else {
yading@11 155 ret = fseeko(infile, atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR);
yading@11 156 }
yading@11 157 if(ret) {
yading@11 158 perror(argv[1]);
yading@11 159 goto error_out;
yading@11 160 }
yading@11 161 }
yading@11 162 printf("%c%c%c%c %10"PRIu64" %"PRIu64"\n",
yading@11 163 (atom_type >> 24) & 255,
yading@11 164 (atom_type >> 16) & 255,
yading@11 165 (atom_type >> 8) & 255,
yading@11 166 (atom_type >> 0) & 255,
yading@11 167 atom_offset,
yading@11 168 atom_size);
yading@11 169 if ((atom_type != FREE_ATOM) &&
yading@11 170 (atom_type != JUNK_ATOM) &&
yading@11 171 (atom_type != MDAT_ATOM) &&
yading@11 172 (atom_type != MOOV_ATOM) &&
yading@11 173 (atom_type != PNOT_ATOM) &&
yading@11 174 (atom_type != SKIP_ATOM) &&
yading@11 175 (atom_type != WIDE_ATOM) &&
yading@11 176 (atom_type != PICT_ATOM) &&
yading@11 177 (atom_type != UUID_ATOM) &&
yading@11 178 (atom_type != FTYP_ATOM)) {
yading@11 179 printf("encountered non-QT top-level atom (is this a QuickTime file?)\n");
yading@11 180 break;
yading@11 181 }
yading@11 182 atom_offset += atom_size;
yading@11 183
yading@11 184 /* The atom header is 8 (or 16 bytes), if the atom size (which
yading@11 185 * includes these 8 or 16 bytes) is less than that, we won't be
yading@11 186 * able to continue scanning sensibly after this atom, so break. */
yading@11 187 if (atom_size < 8)
yading@11 188 break;
yading@11 189 }
yading@11 190
yading@11 191 if (atom_type != MOOV_ATOM) {
yading@11 192 printf("last atom in file was not a moov atom\n");
yading@11 193 free(ftyp_atom);
yading@11 194 fclose(infile);
yading@11 195 return 0;
yading@11 196 }
yading@11 197
yading@11 198 /* moov atom was, in fact, the last atom in the chunk; load the whole
yading@11 199 * moov atom */
yading@11 200 if (fseeko(infile, -atom_size, SEEK_END)) {
yading@11 201 perror(argv[1]);
yading@11 202 goto error_out;
yading@11 203 }
yading@11 204 last_offset = ftello(infile);
yading@11 205 moov_atom_size = atom_size;
yading@11 206 moov_atom = malloc(moov_atom_size);
yading@11 207 if (!moov_atom) {
yading@11 208 printf("could not allocate %"PRIu64" bytes for moov atom\n", atom_size);
yading@11 209 goto error_out;
yading@11 210 }
yading@11 211 if (fread(moov_atom, atom_size, 1, infile) != 1) {
yading@11 212 perror(argv[1]);
yading@11 213 goto error_out;
yading@11 214 }
yading@11 215
yading@11 216 /* this utility does not support compressed atoms yet, so disqualify
yading@11 217 * files with compressed QT atoms */
yading@11 218 if (BE_32(&moov_atom[12]) == CMOV_ATOM) {
yading@11 219 printf("this utility does not support compressed moov atoms yet\n");
yading@11 220 goto error_out;
yading@11 221 }
yading@11 222
yading@11 223 /* close; will be re-opened later */
yading@11 224 fclose(infile);
yading@11 225 infile = NULL;
yading@11 226
yading@11 227 /* crawl through the moov chunk in search of stco or co64 atoms */
yading@11 228 for (i = 4; i < moov_atom_size - 4; i++) {
yading@11 229 atom_type = BE_32(&moov_atom[i]);
yading@11 230 if (atom_type == STCO_ATOM) {
yading@11 231 printf(" patching stco atom...\n");
yading@11 232 atom_size = BE_32(&moov_atom[i - 4]);
yading@11 233 if (i + atom_size - 4 > moov_atom_size) {
yading@11 234 printf(" bad atom size\n");
yading@11 235 goto error_out;
yading@11 236 }
yading@11 237 offset_count = BE_32(&moov_atom[i + 8]);
yading@11 238 if (i + 12LL + offset_count * 4LL > moov_atom_size) {
yading@11 239 printf(" bad atom size\n");
yading@11 240 goto error_out;
yading@11 241 }
yading@11 242 for (j = 0; j < offset_count; j++) {
yading@11 243 current_offset = BE_32(&moov_atom[i + 12 + j * 4]);
yading@11 244 current_offset += moov_atom_size;
yading@11 245 moov_atom[i + 12 + j * 4 + 0] = (current_offset >> 24) & 0xFF;
yading@11 246 moov_atom[i + 12 + j * 4 + 1] = (current_offset >> 16) & 0xFF;
yading@11 247 moov_atom[i + 12 + j * 4 + 2] = (current_offset >> 8) & 0xFF;
yading@11 248 moov_atom[i + 12 + j * 4 + 3] = (current_offset >> 0) & 0xFF;
yading@11 249 }
yading@11 250 i += atom_size - 4;
yading@11 251 } else if (atom_type == CO64_ATOM) {
yading@11 252 printf(" patching co64 atom...\n");
yading@11 253 atom_size = BE_32(&moov_atom[i - 4]);
yading@11 254 if (i + atom_size - 4 > moov_atom_size) {
yading@11 255 printf(" bad atom size\n");
yading@11 256 goto error_out;
yading@11 257 }
yading@11 258 offset_count = BE_32(&moov_atom[i + 8]);
yading@11 259 if (i + 12LL + offset_count * 8LL > moov_atom_size) {
yading@11 260 printf(" bad atom size\n");
yading@11 261 goto error_out;
yading@11 262 }
yading@11 263 for (j = 0; j < offset_count; j++) {
yading@11 264 current_offset = BE_64(&moov_atom[i + 12 + j * 8]);
yading@11 265 current_offset += moov_atom_size;
yading@11 266 moov_atom[i + 12 + j * 8 + 0] = (current_offset >> 56) & 0xFF;
yading@11 267 moov_atom[i + 12 + j * 8 + 1] = (current_offset >> 48) & 0xFF;
yading@11 268 moov_atom[i + 12 + j * 8 + 2] = (current_offset >> 40) & 0xFF;
yading@11 269 moov_atom[i + 12 + j * 8 + 3] = (current_offset >> 32) & 0xFF;
yading@11 270 moov_atom[i + 12 + j * 8 + 4] = (current_offset >> 24) & 0xFF;
yading@11 271 moov_atom[i + 12 + j * 8 + 5] = (current_offset >> 16) & 0xFF;
yading@11 272 moov_atom[i + 12 + j * 8 + 6] = (current_offset >> 8) & 0xFF;
yading@11 273 moov_atom[i + 12 + j * 8 + 7] = (current_offset >> 0) & 0xFF;
yading@11 274 }
yading@11 275 i += atom_size - 4;
yading@11 276 }
yading@11 277 }
yading@11 278
yading@11 279 /* re-open the input file and open the output file */
yading@11 280 infile = fopen(argv[1], "rb");
yading@11 281 if (!infile) {
yading@11 282 perror(argv[1]);
yading@11 283 goto error_out;
yading@11 284 }
yading@11 285
yading@11 286 if (start_offset > 0) { /* seek after ftyp atom */
yading@11 287 if (fseeko(infile, start_offset, SEEK_SET)) {
yading@11 288 perror(argv[1]);
yading@11 289 goto error_out;
yading@11 290 }
yading@11 291
yading@11 292 last_offset -= start_offset;
yading@11 293 }
yading@11 294
yading@11 295 outfile = fopen(argv[2], "wb");
yading@11 296 if (!outfile) {
yading@11 297 perror(argv[2]);
yading@11 298 goto error_out;
yading@11 299 }
yading@11 300
yading@11 301 /* dump the same ftyp atom */
yading@11 302 if (ftyp_atom_size > 0) {
yading@11 303 printf(" writing ftyp atom...\n");
yading@11 304 if (fwrite(ftyp_atom, ftyp_atom_size, 1, outfile) != 1) {
yading@11 305 perror(argv[2]);
yading@11 306 goto error_out;
yading@11 307 }
yading@11 308 }
yading@11 309
yading@11 310 /* dump the new moov atom */
yading@11 311 printf(" writing moov atom...\n");
yading@11 312 if (fwrite(moov_atom, moov_atom_size, 1, outfile) != 1) {
yading@11 313 perror(argv[2]);
yading@11 314 goto error_out;
yading@11 315 }
yading@11 316
yading@11 317 /* copy the remainder of the infile, from offset 0 -> last_offset - 1 */
yading@11 318 bytes_to_copy = FFMIN(COPY_BUFFER_SIZE, last_offset);
yading@11 319 copy_buffer = malloc(bytes_to_copy);
yading@11 320 if (!copy_buffer) {
yading@11 321 printf("could not allocate %d bytes for copy_buffer\n", bytes_to_copy);
yading@11 322 goto error_out;
yading@11 323 }
yading@11 324 printf(" copying rest of file...\n");
yading@11 325 while (last_offset) {
yading@11 326 bytes_to_copy = FFMIN(bytes_to_copy, last_offset);
yading@11 327
yading@11 328 if (fread(copy_buffer, bytes_to_copy, 1, infile) != 1) {
yading@11 329 perror(argv[1]);
yading@11 330 goto error_out;
yading@11 331 }
yading@11 332 if (fwrite(copy_buffer, bytes_to_copy, 1, outfile) != 1) {
yading@11 333 perror(argv[2]);
yading@11 334 goto error_out;
yading@11 335 }
yading@11 336 last_offset -= bytes_to_copy;
yading@11 337 }
yading@11 338
yading@11 339 fclose(infile);
yading@11 340 fclose(outfile);
yading@11 341 free(moov_atom);
yading@11 342 free(ftyp_atom);
yading@11 343 free(copy_buffer);
yading@11 344
yading@11 345 return 0;
yading@11 346
yading@11 347 error_out:
yading@11 348 if (infile)
yading@11 349 fclose(infile);
yading@11 350 if (outfile)
yading@11 351 fclose(outfile);
yading@11 352 free(moov_atom);
yading@11 353 free(ftyp_atom);
yading@11 354 free(copy_buffer);
yading@11 355 return 1;
yading@11 356 }