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