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