~ubuntu-branches/ubuntu/quantal/gst-plugins-bad-multiverse0.10/quantal

« back to all changes in this revision

Viewing changes to gst/qtmux/atomsrecovery.c

  • Committer: Bazaar Package Importer
  • Author(s): Onkar Shinde
  • Date: 2010-03-13 22:48:10 UTC
  • mfrom: (1.1.16 upstream)
  • Revision ID: james.westby@ubuntu.com-20100313224810-8l25xl017imj7z4l
Tags: 0.10.18-0ubuntu1
* New upstream bugfix release.
* Relevant upstream fixes
  - 598350 : qtmux with AAC streams (from faac) generate invalid files
  - 607105 : faac doesn't negotiate channel positions correctly
  - 606726 : FAAC bitrate setting has no effect

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Quicktime muxer plugin for GStreamer
 
2
 * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Library General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Library General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Library General Public
 
15
 * License along with this library; if not, write to the
 
16
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
17
 * Boston, MA 02111-1307, USA.
 
18
 */
 
19
/*
 
20
 * Unless otherwise indicated, Source Code is licensed under MIT license.
 
21
 * See further explanation attached in License Statement (distributed in the file
 
22
 * LICENSE).
 
23
 *
 
24
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 
25
 * this software and associated documentation files (the "Software"), to deal in
 
26
 * the Software without restriction, including without limitation the rights to
 
27
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 
28
 * of the Software, and to permit persons to whom the Software is furnished to do
 
29
 * so, subject to the following conditions:
 
30
 *
 
31
 * The above copyright notice and this permission notice shall be included in all
 
32
 * copies or substantial portions of the Software.
 
33
 *
 
34
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
35
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
36
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
37
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
38
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
39
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
40
 * SOFTWARE.
 
41
 */
 
42
 
 
43
/**
 
44
 * This module contains functions for serializing partial information from
 
45
 * a mux in progress (by qtmux elements). This enables reconstruction of the
 
46
 * moov box if a crash happens and thus recovering the movie file.
 
47
 *
 
48
 * Usage:
 
49
 * 1) pipeline: ...yourelements ! qtmux moov-recovery-file=path.mrf ! \
 
50
 * filesink location=moovie.mov
 
51
 *
 
52
 * 2) CRASH!
 
53
 *
 
54
 * 3) gst-launch qtmoovrecover recovery-input=path.mrf broken-input=moovie.mov \
 
55
        fixed-output=recovered.mov
 
56
 *
 
57
 * 4) (Hopefully) enjoy recovered.mov.
 
58
 *
 
59
 * --- Recovery file layout ---
 
60
 * 1) Version (a guint16)
 
61
 * 2) Prefix atom (if present)
 
62
 * 3) ftyp atom
 
63
 * 4) MVHD atom (without timescale/duration set)
 
64
 * 5) moovie timescale
 
65
 * 6) number of traks
 
66
 * 7) list of trak atoms (stbl data is ignored, except for the stsd atom)
 
67
 * 8) Buffers metadata (metadata that is relevant to the container)
 
68
 *    Buffers metadata are stored in the order they are added to the mdat,
 
69
 *    each entre has a fixed size and is stored in BE. booleans are stored
 
70
 *    as a single byte where 0 means false, otherwise is true.
 
71
 *   Metadata:
 
72
 *   - guint32   track_id;
 
73
 *   - guint32   nsamples;
 
74
 *   - guint32   delta;
 
75
 *   - guint32   size;
 
76
 *   - guint64   chunk_offset;
 
77
 *   - gboolean  sync;
 
78
 *   - gboolean  do_pts;
 
79
 *   - guint64   pts_offset; (always present, ignored if do_pts is false)
 
80
 *
 
81
 * The mdat file might contain ftyp and then mdat, in case this is the faststart
 
82
 * temporary file there is no ftyp and no mdat header, only the buffers data.
 
83
 *
 
84
 * Notes about recovery file layout: We still don't store tags nor EDTS data.
 
85
 *
 
86
 * IMPORTANT: this is still at a experimental state.
 
87
 */
 
88
 
 
89
#include "atomsrecovery.h"
 
90
 
 
91
#define ATOMS_RECOV_OUTPUT_WRITE_ERROR(err) \
 
92
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, \
 
93
        "Failed to write to output file: %s", g_strerror (errno))
 
94
 
 
95
static gboolean
 
96
atoms_recov_write_version (FILE * f)
 
97
{
 
98
  guint8 data[2];
 
99
  GST_WRITE_UINT16_BE (data, ATOMS_RECOV_FILE_VERSION);
 
100
  return fwrite (data, 2, 1, f) == 1;
 
101
}
 
102
 
 
103
static gboolean
 
104
atoms_recov_write_ftyp_info (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix)
 
105
{
 
106
  guint8 *data = NULL;
 
107
  guint64 offset = 0;
 
108
  guint64 size = 0;
 
109
 
 
110
  if (prefix) {
 
111
    if (fwrite (GST_BUFFER_DATA (prefix), 1, GST_BUFFER_SIZE (prefix), f) !=
 
112
        GST_BUFFER_SIZE (prefix)) {
 
113
      return FALSE;
 
114
    }
 
115
  }
 
116
  if (!atom_ftyp_copy_data (ftyp, &data, &size, &offset)) {
 
117
    return FALSE;
 
118
  }
 
119
  if (fwrite (data, 1, offset, f) != offset) {
 
120
    return FALSE;
 
121
  }
 
122
  return TRUE;
 
123
}
 
124
 
 
125
/**
 
126
 * Writes important info on the 'moov' atom (non-trak related)
 
127
 * to be able to recover the moov structure after a crash.
 
128
 *
 
129
 * Currently, it writes the MVHD atom.
 
130
 */
 
131
static gboolean
 
132
atoms_recov_write_moov_info (FILE * f, AtomMOOV * moov)
 
133
{
 
134
  guint8 *data;
 
135
  guint64 size;
 
136
  guint64 offset = 0;
 
137
  guint64 atom_size = 0;
 
138
  gint writen = 0;
 
139
 
 
140
  /* likely enough */
 
141
  size = 256;
 
142
  data = g_malloc (size);
 
143
  atom_size = atom_mvhd_copy_data (&moov->mvhd, &data, &size, &offset);
 
144
  if (atom_size > 0)
 
145
    writen = fwrite (data, 1, atom_size, f);
 
146
  g_free (data);
 
147
  return atom_size > 0 && writen == atom_size;
 
148
}
 
149
 
 
150
/**
 
151
 * Writes the number of traks to the file.
 
152
 * This simply writes a guint32 in BE.
 
153
 */
 
154
static gboolean
 
155
atoms_recov_write_traks_number (FILE * f, guint32 traks)
 
156
{
 
157
  guint8 data[4];
 
158
  GST_WRITE_UINT32_BE (data, traks);
 
159
  return fwrite (data, 4, 1, f) == 1;
 
160
}
 
161
 
 
162
/**
 
163
 * Writes the moov's timescale to the file
 
164
 * This simply writes a guint32 in BE.
 
165
 */
 
166
static gboolean
 
167
atoms_recov_write_moov_timescale (FILE * f, guint32 timescale)
 
168
{
 
169
  guint8 data[4];
 
170
  GST_WRITE_UINT32_BE (data, timescale);
 
171
  return fwrite (data, 4, 1, f) == 1;
 
172
}
 
173
 
 
174
/**
 
175
 * Writes the trak atom to the file.
 
176
 */
 
177
gboolean
 
178
atoms_recov_write_trak_info (FILE * f, AtomTRAK * trak)
 
179
{
 
180
  guint8 *data;
 
181
  guint64 size;
 
182
  guint64 offset = 0;
 
183
  guint64 atom_size = 0;
 
184
  gint writen = 0;
 
185
 
 
186
  /* buffer is realloced to a larger size if needed */
 
187
  size = 4 * 1024;
 
188
  data = g_malloc (size);
 
189
  atom_size = atom_trak_copy_data (trak, &data, &size, &offset);
 
190
  if (atom_size > 0)
 
191
    writen = fwrite (data, atom_size, 1, f);
 
192
  g_free (data);
 
193
  return atom_size > 0 && writen == atom_size;
 
194
}
 
195
 
 
196
gboolean
 
197
atoms_recov_write_trak_samples (FILE * f, AtomTRAK * trak, guint32 nsamples,
 
198
    guint32 delta, guint32 size, guint64 chunk_offset, gboolean sync,
 
199
    gboolean do_pts, gint64 pts_offset)
 
200
{
 
201
  guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE];
 
202
  /*
 
203
   * We have to write a TrakBufferEntryInfo
 
204
   */
 
205
  GST_WRITE_UINT32_BE (data + 0, trak->tkhd.track_ID);
 
206
  GST_WRITE_UINT32_BE (data + 4, nsamples);
 
207
  GST_WRITE_UINT32_BE (data + 8, delta);
 
208
  GST_WRITE_UINT32_BE (data + 12, size);
 
209
  GST_WRITE_UINT64_BE (data + 16, chunk_offset);
 
210
  if (sync)
 
211
    GST_WRITE_UINT8 (data + 24, 1);
 
212
  else
 
213
    GST_WRITE_UINT8 (data + 24, 0);
 
214
  if (do_pts) {
 
215
    GST_WRITE_UINT8 (data + 25, 1);
 
216
    GST_WRITE_UINT64_BE (data + 26, pts_offset);
 
217
  } else {
 
218
    GST_WRITE_UINT8 (data + 25, 0);
 
219
    GST_WRITE_UINT64_BE (data + 26, 0);
 
220
  }
 
221
 
 
222
  return fwrite (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, f) ==
 
223
      TRAK_BUFFER_ENTRY_INFO_SIZE;
 
224
}
 
225
 
 
226
gboolean
 
227
atoms_recov_write_headers (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix,
 
228
    AtomMOOV * moov, guint32 timescale, guint32 traks_number)
 
229
{
 
230
  if (!atoms_recov_write_version (f)) {
 
231
    return FALSE;
 
232
  }
 
233
 
 
234
  if (!atoms_recov_write_ftyp_info (f, ftyp, prefix)) {
 
235
    return FALSE;
 
236
  }
 
237
 
 
238
  if (!atoms_recov_write_moov_info (f, moov)) {
 
239
    return FALSE;
 
240
  }
 
241
 
 
242
  if (!atoms_recov_write_moov_timescale (f, timescale)) {
 
243
    return FALSE;
 
244
  }
 
245
 
 
246
  if (!atoms_recov_write_traks_number (f, traks_number)) {
 
247
    return FALSE;
 
248
  }
 
249
 
 
250
  return TRUE;
 
251
}
 
252
 
 
253
static gboolean
 
254
read_atom_header (FILE * f, guint32 * fourcc, guint32 * size)
 
255
{
 
256
  guint8 aux[8];
 
257
 
 
258
  if (fread (aux, 1, 8, f) != 8)
 
259
    return FALSE;
 
260
  *size = GST_READ_UINT32_BE (aux);
 
261
  *fourcc = GST_READ_UINT32_LE (aux + 4);
 
262
  return TRUE;
 
263
}
 
264
 
 
265
static gboolean
 
266
moov_recov_file_parse_prefix (MoovRecovFile * moovrf)
 
267
{
 
268
  guint32 fourcc;
 
269
  guint32 size;
 
270
  guint32 total_size = 0;
 
271
  if (fseek (moovrf->file, 2, SEEK_SET) != 0)
 
272
    return FALSE;
 
273
  if (!read_atom_header (moovrf->file, &fourcc, &size)) {
 
274
    return FALSE;
 
275
  }
 
276
 
 
277
  if (fourcc != FOURCC_ftyp) {
 
278
    /* we might have a prefix here */
 
279
    if (fseek (moovrf->file, size - 8, SEEK_CUR) != 0)
 
280
      return FALSE;
 
281
 
 
282
    total_size += size;
 
283
 
 
284
    /* now read the ftyp */
 
285
    if (!read_atom_header (moovrf->file, &fourcc, &size))
 
286
      return FALSE;
 
287
  }
 
288
 
 
289
  /* this has to be the ftyp */
 
290
  if (fourcc != FOURCC_ftyp)
 
291
    return FALSE;
 
292
  total_size += size;
 
293
  moovrf->prefix_size = total_size;
 
294
  return fseek (moovrf->file, size - 8, SEEK_CUR) == 0;
 
295
}
 
296
 
 
297
static gboolean
 
298
moov_recov_file_parse_mvhd (MoovRecovFile * moovrf)
 
299
{
 
300
  guint32 fourcc;
 
301
  guint32 size;
 
302
  if (!read_atom_header (moovrf->file, &fourcc, &size)) {
 
303
    return FALSE;
 
304
  }
 
305
  /* check for sanity */
 
306
  if (fourcc != FOURCC_mvhd)
 
307
    return FALSE;
 
308
 
 
309
  moovrf->mvhd_size = size;
 
310
  moovrf->mvhd_pos = ftell (moovrf->file) - 8;
 
311
 
 
312
  /* skip the remaining of the mvhd in the file */
 
313
  return fseek (moovrf->file, size - 8, SEEK_CUR) == 0;
 
314
}
 
315
 
 
316
static gboolean
 
317
mdat_recov_file_parse_mdat_start (MdatRecovFile * mdatrf)
 
318
{
 
319
  guint32 fourcc, size;
 
320
 
 
321
  if (!read_atom_header (mdatrf->file, &fourcc, &size)) {
 
322
    return FALSE;
 
323
  }
 
324
  if (size == 1) {
 
325
    mdatrf->mdat_header_size = 16;
 
326
    mdatrf->mdat_size = 16;
 
327
  } else {
 
328
    mdatrf->mdat_header_size = 8;
 
329
    mdatrf->mdat_size = 8;
 
330
  }
 
331
  mdatrf->mdat_start = ftell (mdatrf->file) - 8;
 
332
 
 
333
  return fourcc == FOURCC_mdat;
 
334
}
 
335
 
 
336
MdatRecovFile *
 
337
mdat_recov_file_create (FILE * file, gboolean datafile, GError ** err)
 
338
{
 
339
  MdatRecovFile *mrf = g_new0 (MdatRecovFile, 1);
 
340
  guint32 fourcc, size;
 
341
 
 
342
  g_return_val_if_fail (file != NULL, NULL);
 
343
 
 
344
  mrf->file = file;
 
345
  mrf->rawfile = datafile;
 
346
 
 
347
  /* get the file/data length */
 
348
  if (fseek (file, 0, SEEK_END) != 0)
 
349
    goto file_length_error;
 
350
  /* still needs to deduce the mdat header and ftyp size */
 
351
  mrf->data_size = ftell (file);
 
352
  if (mrf->data_size == -1L)
 
353
    goto file_length_error;
 
354
 
 
355
  if (fseek (file, 0, SEEK_SET) != 0)
 
356
    goto file_seek_error;
 
357
 
 
358
  if (datafile) {
 
359
    /* this file contains no atoms, only raw data to be placed on the mdat
 
360
     * this happens when faststart mode is used */
 
361
    mrf->mdat_start = 0;
 
362
    mrf->mdat_header_size = 16;
 
363
    mrf->mdat_size = 16;
 
364
    return mrf;
 
365
  }
 
366
 
 
367
  if (!read_atom_header (file, &fourcc, &size)) {
 
368
    goto parse_error;
 
369
  }
 
370
  if (fourcc != FOURCC_ftyp) {
 
371
    /* this could be a prefix atom, let's skip it and try again */
 
372
    if (fseek (file, size - 8, SEEK_CUR) != 0) {
 
373
      goto file_seek_error;
 
374
    }
 
375
    if (!read_atom_header (file, &fourcc, &size)) {
 
376
      goto parse_error;
 
377
    }
 
378
  }
 
379
 
 
380
  if (fourcc != FOURCC_ftyp) {
 
381
    goto parse_error;
 
382
  }
 
383
  if (fseek (file, size - 8, SEEK_CUR) != 0)
 
384
    goto file_seek_error;
 
385
 
 
386
  /* we don't parse this if we have a tmpdatafile */
 
387
  if (!mdat_recov_file_parse_mdat_start (mrf)) {
 
388
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
 
389
        "Error while parsing mdat atom");
 
390
    goto fail;
 
391
  }
 
392
 
 
393
  return mrf;
 
394
 
 
395
parse_error:
 
396
  g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
 
397
      "Failed to parse atom");
 
398
  goto fail;
 
399
 
 
400
file_seek_error:
 
401
  g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
 
402
      "Failed to seek to start of the file");
 
403
  goto fail;
 
404
 
 
405
file_length_error:
 
406
  g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
 
407
      "Failed to determine file size");
 
408
  goto fail;
 
409
 
 
410
fail:
 
411
  mdat_recov_file_free (mrf);
 
412
  return NULL;
 
413
}
 
414
 
 
415
void
 
416
mdat_recov_file_free (MdatRecovFile * mrf)
 
417
{
 
418
  fclose (mrf->file);
 
419
  g_free (mrf);
 
420
}
 
421
 
 
422
static gboolean
 
423
moov_recov_parse_num_traks (MoovRecovFile * moovrf)
 
424
{
 
425
  guint8 traks[4];
 
426
  if (fread (traks, 1, 4, moovrf->file) != 4)
 
427
    return FALSE;
 
428
  moovrf->num_traks = GST_READ_UINT32_BE (traks);
 
429
  return TRUE;
 
430
}
 
431
 
 
432
static gboolean
 
433
moov_recov_parse_moov_timescale (MoovRecovFile * moovrf)
 
434
{
 
435
  guint8 ts[4];
 
436
  if (fread (ts, 1, 4, moovrf->file) != 4)
 
437
    return FALSE;
 
438
  moovrf->timescale = GST_READ_UINT32_BE (ts);
 
439
  return TRUE;
 
440
}
 
441
 
 
442
static gboolean
 
443
skip_atom (MoovRecovFile * moovrf, guint32 expected_fourcc)
 
444
{
 
445
  guint32 size;
 
446
  guint32 fourcc;
 
447
 
 
448
  if (!read_atom_header (moovrf->file, &fourcc, &size))
 
449
    return FALSE;
 
450
  if (fourcc != expected_fourcc)
 
451
    return FALSE;
 
452
 
 
453
  return (fseek (moovrf->file, size - 8, SEEK_CUR) == 0);
 
454
}
 
455
 
 
456
static gboolean
 
457
moov_recov_parse_tkhd (MoovRecovFile * moovrf, TrakRecovData * trakrd)
 
458
{
 
459
  guint32 size;
 
460
  guint32 fourcc;
 
461
  guint8 data[4];
 
462
 
 
463
  /* make sure we are on a tkhd atom */
 
464
  if (!read_atom_header (moovrf->file, &fourcc, &size))
 
465
    return FALSE;
 
466
  if (fourcc != FOURCC_tkhd)
 
467
    return FALSE;
 
468
 
 
469
  trakrd->tkhd_file_offset = ftell (moovrf->file) - 8;
 
470
 
 
471
  /* move 8 bytes forward to the trak_id pos */
 
472
  if (fseek (moovrf->file, 12, SEEK_CUR) != 0)
 
473
    return FALSE;
 
474
  if (fread (data, 1, 4, moovrf->file) != 4)
 
475
    return FALSE;
 
476
 
 
477
  /* advance the rest of tkhd */
 
478
  fseek (moovrf->file, 68, SEEK_CUR);
 
479
 
 
480
  trakrd->trak_id = GST_READ_UINT32_BE (data);
 
481
  return TRUE;
 
482
}
 
483
 
 
484
static gboolean
 
485
moov_recov_parse_stbl (MoovRecovFile * moovrf, TrakRecovData * trakrd)
 
486
{
 
487
  guint32 size;
 
488
  guint32 fourcc;
 
489
  guint32 auxsize;
 
490
 
 
491
  if (!read_atom_header (moovrf->file, &fourcc, &size))
 
492
    return FALSE;
 
493
  if (fourcc != FOURCC_stbl)
 
494
    return FALSE;
 
495
 
 
496
  trakrd->stbl_file_offset = ftell (moovrf->file) - 8;
 
497
  trakrd->stbl_size = size;
 
498
 
 
499
  /* skip the stsd */
 
500
  if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
 
501
    return FALSE;
 
502
  if (fourcc != FOURCC_stsd)
 
503
    return FALSE;
 
504
  if (fseek (moovrf->file, auxsize - 8, SEEK_CUR) != 0)
 
505
    return FALSE;
 
506
 
 
507
  trakrd->stsd_size = auxsize;
 
508
  trakrd->post_stsd_offset = ftell (moovrf->file);
 
509
 
 
510
  /* as this is the last atom we parse, we don't skip forward */
 
511
 
 
512
  return TRUE;
 
513
}
 
514
 
 
515
static gboolean
 
516
moov_recov_parse_minf (MoovRecovFile * moovrf, TrakRecovData * trakrd)
 
517
{
 
518
  guint32 size;
 
519
  guint32 fourcc;
 
520
  guint32 auxsize;
 
521
 
 
522
  if (!read_atom_header (moovrf->file, &fourcc, &size))
 
523
    return FALSE;
 
524
  if (fourcc != FOURCC_minf)
 
525
    return FALSE;
 
526
 
 
527
  trakrd->minf_file_offset = ftell (moovrf->file) - 8;
 
528
  trakrd->minf_size = size;
 
529
 
 
530
  /* skip either of vmhd, smhd, hmhd that might follow */
 
531
  if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
 
532
    return FALSE;
 
533
  if (fourcc != FOURCC_vmhd && fourcc != FOURCC_smhd && fourcc != FOURCC_hmhd &&
 
534
      fourcc != FOURCC_gmhd)
 
535
    return FALSE;
 
536
  if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
 
537
    return FALSE;
 
538
 
 
539
  /* skip a possible hdlr and the following dinf */
 
540
  if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
 
541
    return FALSE;
 
542
  if (fourcc == FOURCC_hdlr) {
 
543
    if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
 
544
      return FALSE;
 
545
    if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
 
546
      return FALSE;
 
547
  }
 
548
  if (fourcc != FOURCC_dinf)
 
549
    return FALSE;
 
550
  if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
 
551
    return FALSE;
 
552
 
 
553
  /* now we are ready to read the stbl */
 
554
  if (!moov_recov_parse_stbl (moovrf, trakrd))
 
555
    return FALSE;
 
556
 
 
557
  return TRUE;
 
558
}
 
559
 
 
560
static gboolean
 
561
moov_recov_parse_mdhd (MoovRecovFile * moovrf, TrakRecovData * trakrd)
 
562
{
 
563
  guint32 size;
 
564
  guint32 fourcc;
 
565
  guint8 data[4];
 
566
 
 
567
  /* make sure we are on a tkhd atom */
 
568
  if (!read_atom_header (moovrf->file, &fourcc, &size))
 
569
    return FALSE;
 
570
  if (fourcc != FOURCC_mdhd)
 
571
    return FALSE;
 
572
 
 
573
  trakrd->mdhd_file_offset = ftell (moovrf->file) - 8;
 
574
 
 
575
  /* get the timescale */
 
576
  if (fseek (moovrf->file, 12, SEEK_CUR) != 0)
 
577
    return FALSE;
 
578
  if (fread (data, 1, 4, moovrf->file) != 4)
 
579
    return FALSE;
 
580
  trakrd->timescale = GST_READ_UINT32_BE (data);
 
581
  if (fseek (moovrf->file, 8, SEEK_CUR) != 0)
 
582
    return FALSE;
 
583
  return TRUE;
 
584
}
 
585
 
 
586
static gboolean
 
587
moov_recov_parse_mdia (MoovRecovFile * moovrf, TrakRecovData * trakrd)
 
588
{
 
589
  guint32 size;
 
590
  guint32 fourcc;
 
591
 
 
592
  /* make sure we are on a tkhd atom */
 
593
  if (!read_atom_header (moovrf->file, &fourcc, &size))
 
594
    return FALSE;
 
595
  if (fourcc != FOURCC_mdia)
 
596
    return FALSE;
 
597
 
 
598
  trakrd->mdia_file_offset = ftell (moovrf->file) - 8;
 
599
  trakrd->mdia_size = size;
 
600
 
 
601
  if (!moov_recov_parse_mdhd (moovrf, trakrd))
 
602
    return FALSE;
 
603
 
 
604
  if (!skip_atom (moovrf, FOURCC_hdlr))
 
605
    return FALSE;
 
606
  if (!moov_recov_parse_minf (moovrf, trakrd))
 
607
    return FALSE;
 
608
  return TRUE;
 
609
}
 
610
 
 
611
static gboolean
 
612
moov_recov_parse_trak (MoovRecovFile * moovrf, TrakRecovData * trakrd)
 
613
{
 
614
  guint64 offset;
 
615
  guint32 size;
 
616
  guint32 fourcc;
 
617
 
 
618
  offset = ftell (moovrf->file);
 
619
  if (offset == -1) {
 
620
    return FALSE;
 
621
  }
 
622
 
 
623
  /* make sure we are on a trak atom */
 
624
  if (!read_atom_header (moovrf->file, &fourcc, &size)) {
 
625
    return FALSE;
 
626
  }
 
627
  if (fourcc != FOURCC_trak) {
 
628
    return FALSE;
 
629
  }
 
630
  trakrd->trak_size = size;
 
631
 
 
632
  /* now we should have a trak header 'tkhd' */
 
633
  if (!moov_recov_parse_tkhd (moovrf, trakrd))
 
634
    return FALSE;
 
635
 
 
636
  /* FIXME add edts handling here and in qtmux, as this is only detected
 
637
   * after buffers start flowing */
 
638
 
 
639
  if (!moov_recov_parse_mdia (moovrf, trakrd))
 
640
    return FALSE;
 
641
 
 
642
  trakrd->file_offset = offset;
 
643
  /* position after the trak */
 
644
  return fseek (moovrf->file, (long int) offset + size, SEEK_SET) == 0;
 
645
}
 
646
 
 
647
MoovRecovFile *
 
648
moov_recov_file_create (FILE * file, GError ** err)
 
649
{
 
650
  gint i;
 
651
  MoovRecovFile *moovrf = g_new0 (MoovRecovFile, 1);
 
652
 
 
653
  g_return_val_if_fail (file != NULL, NULL);
 
654
 
 
655
  moovrf->file = file;
 
656
 
 
657
  /* look for ftyp and prefix at the start */
 
658
  if (!moov_recov_file_parse_prefix (moovrf)) {
 
659
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
 
660
        "Error while parsing prefix atoms");
 
661
    goto fail;
 
662
  }
 
663
 
 
664
  /* parse the mvhd */
 
665
  if (!moov_recov_file_parse_mvhd (moovrf)) {
 
666
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
 
667
        "Error while parsing mvhd atom");
 
668
    goto fail;
 
669
  }
 
670
 
 
671
  if (!moov_recov_parse_moov_timescale (moovrf)) {
 
672
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
 
673
        "Error while parsing timescale");
 
674
    goto fail;
 
675
  }
 
676
  if (!moov_recov_parse_num_traks (moovrf)) {
 
677
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
 
678
        "Error while parsing parsing number of traks");
 
679
    goto fail;
 
680
  }
 
681
 
 
682
  /* init the traks */
 
683
  moovrf->traks_rd = g_new0 (TrakRecovData, moovrf->num_traks);
 
684
  for (i = 0; i < moovrf->num_traks; i++) {
 
685
    atom_stbl_init (&(moovrf->traks_rd[i].stbl));
 
686
  }
 
687
  for (i = 0; i < moovrf->num_traks; i++) {
 
688
    if (!moov_recov_parse_trak (moovrf, &(moovrf->traks_rd[i]))) {
 
689
      g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
 
690
          "Error while parsing trak atom");
 
691
      goto fail;
 
692
    }
 
693
  }
 
694
 
 
695
  return moovrf;
 
696
 
 
697
fail:
 
698
  moov_recov_file_free (moovrf);
 
699
  return NULL;
 
700
}
 
701
 
 
702
void
 
703
moov_recov_file_free (MoovRecovFile * moovrf)
 
704
{
 
705
  gint i;
 
706
  fclose (moovrf->file);
 
707
  if (moovrf->traks_rd) {
 
708
    for (i = 0; i < moovrf->num_traks; i++) {
 
709
      atom_stbl_clear (&(moovrf->traks_rd[i].stbl));
 
710
    }
 
711
    g_free (moovrf->traks_rd);
 
712
  }
 
713
  g_free (moovrf);
 
714
}
 
715
 
 
716
static gboolean
 
717
moov_recov_parse_buffer_entry (MoovRecovFile * moovrf, TrakBufferEntryInfo * b)
 
718
{
 
719
  guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE];
 
720
  gint read;
 
721
 
 
722
  read = fread (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, moovrf->file);
 
723
  if (read != TRAK_BUFFER_ENTRY_INFO_SIZE)
 
724
    return FALSE;
 
725
 
 
726
  b->track_id = GST_READ_UINT32_BE (data);
 
727
  b->nsamples = GST_READ_UINT32_BE (data + 4);
 
728
  b->delta = GST_READ_UINT32_BE (data + 8);
 
729
  b->size = GST_READ_UINT32_BE (data + 12);
 
730
  b->chunk_offset = GST_READ_UINT64_BE (data + 16);
 
731
  b->sync = data[24] != 0;
 
732
  b->do_pts = data[25] != 0;
 
733
  b->pts_offset = GST_READ_UINT64_BE (data + 26);
 
734
  return TRUE;
 
735
}
 
736
 
 
737
static gboolean
 
738
mdat_recov_add_sample (MdatRecovFile * mdatrf, guint32 size)
 
739
{
 
740
  /* test if this data exists */
 
741
  if (mdatrf->mdat_size - mdatrf->mdat_header_size + size > mdatrf->data_size)
 
742
    return FALSE;
 
743
 
 
744
  mdatrf->mdat_size += size;
 
745
  return TRUE;
 
746
}
 
747
 
 
748
static TrakRecovData *
 
749
moov_recov_get_trak (MoovRecovFile * moovrf, guint32 id)
 
750
{
 
751
  gint i;
 
752
  for (i = 0; i < moovrf->num_traks; i++) {
 
753
    if (moovrf->traks_rd[i].trak_id == id)
 
754
      return &(moovrf->traks_rd[i]);
 
755
  }
 
756
  return NULL;
 
757
}
 
758
 
 
759
static void
 
760
trak_recov_data_add_sample (TrakRecovData * trak, TrakBufferEntryInfo * b)
 
761
{
 
762
  trak->duration += b->nsamples * b->delta;
 
763
  atom_stbl_add_samples (&trak->stbl, b->nsamples, b->delta, b->size,
 
764
      b->chunk_offset, b->sync, b->do_pts, b->pts_offset);
 
765
}
 
766
 
 
767
/**
 
768
 * Parses the buffer entries in the MoovRecovFile and matches the inputs
 
769
 * with the data in the MdatRecovFile. Whenever a buffer entry of that
 
770
 * represents 'x' bytes of data, the same amount of data is 'validated' in
 
771
 * the MdatRecovFile and will be inluded in the generated moovie file.
 
772
 */
 
773
gboolean
 
774
moov_recov_parse_buffers (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
 
775
    GError ** err)
 
776
{
 
777
  TrakBufferEntryInfo entry;
 
778
  TrakRecovData *trak;
 
779
 
 
780
  /* we assume both moovrf and mdatrf are at the starting points of their
 
781
   * data reading */
 
782
  while (moov_recov_parse_buffer_entry (moovrf, &entry)) {
 
783
    /* be sure we still have this data in mdat */
 
784
    trak = moov_recov_get_trak (moovrf, entry.track_id);
 
785
    if (trak == NULL) {
 
786
      g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
 
787
          "Invalid trak id found in buffer entry");
 
788
      return FALSE;
 
789
    }
 
790
    if (!mdat_recov_add_sample (mdatrf, entry.size))
 
791
      break;
 
792
    trak_recov_data_add_sample (trak, &entry);
 
793
  }
 
794
  return TRUE;
 
795
}
 
796
 
 
797
guint32
 
798
trak_recov_data_get_trak_atom_size (TrakRecovData * trak)
 
799
{
 
800
  AtomSTBL *stbl = &trak->stbl;
 
801
  guint64 offset;
 
802
 
 
803
  /* write out our stbl child atoms */
 
804
  offset = 0;
 
805
 
 
806
  if (!atom_stts_copy_data (&stbl->stts, NULL, NULL, &offset)) {
 
807
    goto fail;
 
808
  }
 
809
  if (atom_array_get_len (&stbl->stss.entries) > 0) {
 
810
    if (!atom_stss_copy_data (&stbl->stss, NULL, NULL, &offset)) {
 
811
      goto fail;
 
812
    }
 
813
  }
 
814
  if (!atom_stsc_copy_data (&stbl->stsc, NULL, NULL, &offset)) {
 
815
    goto fail;
 
816
  }
 
817
  if (!atom_stsz_copy_data (&stbl->stsz, NULL, NULL, &offset)) {
 
818
    goto fail;
 
819
  }
 
820
  if (stbl->ctts) {
 
821
    if (!atom_ctts_copy_data (stbl->ctts, NULL, NULL, &offset)) {
 
822
      goto fail;
 
823
    }
 
824
  }
 
825
  if (!atom_stco64_copy_data (&stbl->stco64, NULL, NULL, &offset)) {
 
826
    goto fail;
 
827
  }
 
828
 
 
829
  return trak->trak_size + ((trak->stsd_size + offset + 8) - trak->stbl_size);
 
830
 
 
831
fail:
 
832
  return 0;
 
833
}
 
834
 
 
835
guint8 *
 
836
moov_recov_get_stbl_children_data (MoovRecovFile * moovrf, TrakRecovData * trak,
 
837
    guint64 * p_size)
 
838
{
 
839
  AtomSTBL *stbl = &trak->stbl;
 
840
  guint8 *buffer;
 
841
  guint64 size;
 
842
  guint64 offset;
 
843
 
 
844
  /* write out our stbl child atoms
 
845
   *
 
846
   * Use 1MB as a starting size, *_copy_data functions
 
847
   * will grow the buffer if needed.
 
848
   */
 
849
  size = 1024 * 1024;
 
850
  buffer = g_malloc0 (size);
 
851
  offset = 0;
 
852
 
 
853
  if (!atom_stts_copy_data (&stbl->stts, &buffer, &size, &offset)) {
 
854
    goto fail;
 
855
  }
 
856
  if (atom_array_get_len (&stbl->stss.entries) > 0) {
 
857
    if (!atom_stss_copy_data (&stbl->stss, &buffer, &size, &offset)) {
 
858
      goto fail;
 
859
    }
 
860
  }
 
861
  if (!atom_stsc_copy_data (&stbl->stsc, &buffer, &size, &offset)) {
 
862
    goto fail;
 
863
  }
 
864
  if (!atom_stsz_copy_data (&stbl->stsz, &buffer, &size, &offset)) {
 
865
    goto fail;
 
866
  }
 
867
  if (stbl->ctts) {
 
868
    if (!atom_ctts_copy_data (stbl->ctts, &buffer, &size, &offset)) {
 
869
      goto fail;
 
870
    }
 
871
  }
 
872
  if (!atom_stco64_copy_data (&stbl->stco64, &buffer, &size, &offset)) {
 
873
    goto fail;
 
874
  }
 
875
  *p_size = offset;
 
876
  return buffer;
 
877
 
 
878
fail:
 
879
  g_free (buffer);
 
880
  return NULL;
 
881
}
 
882
 
 
883
gboolean
 
884
moov_recov_write_file (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
 
885
    FILE * outf, GError ** err)
 
886
{
 
887
  guint8 auxdata[16];
 
888
  guint8 *data = NULL;
 
889
  guint8 *prefix_data = NULL;
 
890
  guint8 *mvhd_data = NULL;
 
891
  guint8 *trak_data = NULL;
 
892
  guint32 moov_size = 0;
 
893
  gint i;
 
894
  guint64 stbl_children_size = 0;
 
895
  guint8 *stbl_children = NULL;
 
896
  guint32 longest_duration = 0;
 
897
  guint16 version;
 
898
 
 
899
  /* check the version */
 
900
  if (fseek (moovrf->file, 0, SEEK_SET) != 0) {
 
901
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
 
902
        "Failed to seek to the start of the moov recovery file");
 
903
    goto fail;
 
904
  }
 
905
  if (fread (auxdata, 1, 2, moovrf->file) != 2) {
 
906
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
 
907
        "Failed to read version from file");
 
908
  }
 
909
 
 
910
  version = GST_READ_UINT16_BE (auxdata);
 
911
  if (version != ATOMS_RECOV_FILE_VERSION) {
 
912
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_VERSION,
 
913
        "Input file version (%u) is not supported in this version (%u)",
 
914
        version, ATOMS_RECOV_FILE_VERSION);
 
915
    return FALSE;
 
916
  }
 
917
 
 
918
  /* write the ftyp */
 
919
  prefix_data = g_malloc (moovrf->prefix_size);
 
920
  if (fread (prefix_data, 1, moovrf->prefix_size,
 
921
          moovrf->file) != moovrf->prefix_size) {
 
922
    g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
 
923
        "Failed to read the ftyp atom from file");
 
924
    goto fail;
 
925
  }
 
926
  if (fwrite (prefix_data, 1, moovrf->prefix_size, outf) != moovrf->prefix_size) {
 
927
    ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
 
928
    goto fail;
 
929
  }
 
930
  g_free (prefix_data);
 
931
  prefix_data = NULL;
 
932
 
 
933
  /* need to calculate the moov size beforehand to add the offset to
 
934
   * chunk offset entries */
 
935
  moov_size += moovrf->mvhd_size + 8;   /* mvhd + moov size + fourcc */
 
936
  for (i = 0; i < moovrf->num_traks; i++) {
 
937
    TrakRecovData *trak = &(moovrf->traks_rd[i]);
 
938
    guint32 duration;           /* in moov's timescale */
 
939
    guint32 trak_size;
 
940
 
 
941
    /* convert trak duration to moov's duration */
 
942
    duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale,
 
943
        trak->timescale);
 
944
 
 
945
    if (duration > longest_duration)
 
946
      longest_duration = duration;
 
947
    trak_size = trak_recov_data_get_trak_atom_size (trak);
 
948
    if (trak_size == 0) {
 
949
      g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_GENERIC,
 
950
          "Failed to estimate trak atom size");
 
951
      goto fail;
 
952
    }
 
953
    moov_size += trak_size;
 
954
  }
 
955
 
 
956
  /* add chunks offsets */
 
957
  for (i = 0; i < moovrf->num_traks; i++) {
 
958
    TrakRecovData *trak = &(moovrf->traks_rd[i]);
 
959
    /* 16 for the mdat header */
 
960
    gint64 offset = moov_size + ftell (outf) + 16;
 
961
    atom_stco64_chunks_add_offset (&trak->stbl.stco64, offset);
 
962
  }
 
963
 
 
964
  /* write the moov */
 
965
  GST_WRITE_UINT32_BE (auxdata, moov_size);
 
966
  GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_moov);
 
967
  if (fwrite (auxdata, 1, 8, outf) != 8) {
 
968
    ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
 
969
    goto fail;
 
970
  }
 
971
 
 
972
  /* write the mvhd */
 
973
  mvhd_data = g_malloc (moovrf->mvhd_size);
 
974
  if (fseek (moovrf->file, moovrf->mvhd_pos, SEEK_SET) != 0)
 
975
    goto fail;
 
976
  if (fread (mvhd_data, 1, moovrf->mvhd_size,
 
977
          moovrf->file) != moovrf->mvhd_size)
 
978
    goto fail;
 
979
  GST_WRITE_UINT32_BE (mvhd_data + 20, moovrf->timescale);
 
980
  GST_WRITE_UINT32_BE (mvhd_data + 24, longest_duration);
 
981
  if (fwrite (mvhd_data, 1, moovrf->mvhd_size, outf) != moovrf->mvhd_size) {
 
982
    ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
 
983
    goto fail;
 
984
  }
 
985
  g_free (mvhd_data);
 
986
  mvhd_data = NULL;
 
987
 
 
988
  /* write the traks, this is the tough part because we need to update:
 
989
   * - stbl atom
 
990
   * - sizes of atoms from stbl to trak
 
991
   * - trak duration
 
992
   */
 
993
  for (i = 0; i < moovrf->num_traks; i++) {
 
994
    TrakRecovData *trak = &(moovrf->traks_rd[i]);
 
995
    guint trak_data_size;
 
996
    guint32 stbl_new_size;
 
997
    guint32 minf_new_size;
 
998
    guint32 mdia_new_size;
 
999
    guint32 trak_new_size;
 
1000
    guint32 size_diff;
 
1001
    guint32 duration;           /* in moov's timescale */
 
1002
 
 
1003
    /* convert trak duration to moov's duration */
 
1004
    duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale,
 
1005
        trak->timescale);
 
1006
 
 
1007
    stbl_children = moov_recov_get_stbl_children_data (moovrf, trak,
 
1008
        &stbl_children_size);
 
1009
    if (stbl_children == NULL)
 
1010
      goto fail;
 
1011
 
 
1012
    /* calc the new size of the atoms from stbl to trak in the atoms tree */
 
1013
    stbl_new_size = trak->stsd_size + stbl_children_size + 8;
 
1014
    size_diff = stbl_new_size - trak->stbl_size;
 
1015
    minf_new_size = trak->minf_size + size_diff;
 
1016
    mdia_new_size = trak->mdia_size + size_diff;
 
1017
    trak_new_size = trak->trak_size + size_diff;
 
1018
 
 
1019
    if (fseek (moovrf->file, trak->file_offset, SEEK_SET) != 0)
 
1020
      goto fail;
 
1021
    trak_data_size = trak->post_stsd_offset - trak->file_offset;
 
1022
    trak_data = g_malloc (trak_data_size);
 
1023
    if (fread (trak_data, 1, trak_data_size, moovrf->file) != trak_data_size) {
 
1024
      goto fail;
 
1025
    }
 
1026
    /* update the size values in those read atoms before writing */
 
1027
    GST_WRITE_UINT32_BE (trak_data, trak_new_size);
 
1028
    GST_WRITE_UINT32_BE (trak_data + (trak->mdia_file_offset -
 
1029
            trak->file_offset), mdia_new_size);
 
1030
    GST_WRITE_UINT32_BE (trak_data + (trak->minf_file_offset -
 
1031
            trak->file_offset), minf_new_size);
 
1032
    GST_WRITE_UINT32_BE (trak_data + (trak->stbl_file_offset -
 
1033
            trak->file_offset), stbl_new_size);
 
1034
 
 
1035
    /* update duration values in tkhd and mdhd */
 
1036
    GST_WRITE_UINT32_BE (trak_data + (trak->tkhd_file_offset -
 
1037
            trak->file_offset) + 28, duration);
 
1038
    GST_WRITE_UINT32_BE (trak_data + (trak->mdhd_file_offset -
 
1039
            trak->file_offset) + 24, trak->duration);
 
1040
 
 
1041
    if (fwrite (trak_data, 1, trak_data_size, outf) != trak_data_size) {
 
1042
      ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
 
1043
      goto fail;
 
1044
    }
 
1045
    if (fwrite (stbl_children, 1, stbl_children_size, outf) !=
 
1046
        stbl_children_size) {
 
1047
      ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
 
1048
      goto fail;
 
1049
    }
 
1050
    g_free (trak_data);
 
1051
    trak_data = NULL;
 
1052
    g_free (stbl_children);
 
1053
    stbl_children = NULL;
 
1054
  }
 
1055
 
 
1056
  /* write the mdat */
 
1057
  /* write the header first */
 
1058
  GST_WRITE_UINT32_BE (auxdata, 1);
 
1059
  GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
 
1060
  GST_WRITE_UINT64_BE (auxdata + 8, mdatrf->mdat_size);
 
1061
  if (fwrite (auxdata, 1, 16, outf) != 16) {
 
1062
    ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
 
1063
    goto fail;
 
1064
  }
 
1065
 
 
1066
  /* now read the mdat data and output to the file */
 
1067
  if (fseek (mdatrf->file, mdatrf->mdat_start +
 
1068
          (mdatrf->rawfile ? 0 : mdatrf->mdat_header_size), SEEK_SET) != 0)
 
1069
    goto fail;
 
1070
 
 
1071
  data = g_malloc (4096);
 
1072
  while (!feof (mdatrf->file)) {
 
1073
    gint read, write;
 
1074
 
 
1075
    read = fread (data, 1, 4096, mdatrf->file);
 
1076
    write = fwrite (data, 1, read, outf);
 
1077
 
 
1078
    if (write != read) {
 
1079
      g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
 
1080
          "Failed to copy data to output file: %s", g_strerror (errno));
 
1081
      goto fail;
 
1082
    }
 
1083
  }
 
1084
  g_free (data);
 
1085
 
 
1086
  return TRUE;
 
1087
 
 
1088
fail:
 
1089
  g_free (stbl_children);
 
1090
  g_free (mvhd_data);
 
1091
  g_free (prefix_data);
 
1092
  g_free (trak_data);
 
1093
  g_free (data);
 
1094
  return FALSE;
 
1095
}