1 /* Quicktime muxer plugin for GStreamer
2 * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
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.
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.
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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 * Unless otherwise indicated, Source Code is licensed under MIT license.
21 * See further explanation attached in License Statement (distributed in the file
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:
31 * The above copyright notice and this permission notice shall be included in all
32 * copies or substantial portions of the Software.
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
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.
49 * 1) pipeline: ...yourelements ! qtmux moov-recovery-file=path.mrf ! \
50 * filesink location=moovie.mov
54 * 3) gst-launch-1.0 qtmoovrecover recovery-input=path.mrf broken-input=moovie.mov \
55 fixed-output=recovered.mov
57 * 4) (Hopefully) enjoy recovered.mov.
59 * --- Recovery file layout ---
60 * 1) Version (a guint16)
61 * 2) Prefix atom (if present)
63 * 4) MVHD atom (without timescale/duration set)
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.
76 * - guint64 chunk_offset;
79 * - guint64 pts_offset; (always present, ignored if do_pts is false)
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.
84 * Notes about recovery file layout: We still don't store tags nor EDTS data.
86 * IMPORTANT: this is still at a experimental state.
89 #include "atomsrecovery.h"
91 #define MAX_CHUNK_SIZE (1024 * 1024) /* 1MB */
93 #define ATOMS_RECOV_OUTPUT_WRITE_ERROR(err) \
94 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, \
95 "Failed to write to output file: %s", g_strerror (errno))
98 atoms_recov_write_version (FILE * f)
101 GST_WRITE_UINT16_BE (data, ATOMS_RECOV_FILE_VERSION);
102 return fwrite (data, 2, 1, f) == 1;
106 atoms_recov_write_ftyp_info (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix)
115 gst_buffer_map (prefix, &map, GST_MAP_READ);
116 if (fwrite (map.data, 1, map.size, f) != map.size) {
117 gst_buffer_unmap (prefix, &map);
120 gst_buffer_unmap (prefix, &map);
122 if (!atom_ftyp_copy_data (ftyp, &data, &size, &offset)) {
125 if (fwrite (data, 1, offset, f) != offset) {
134 * Writes important info on the 'moov' atom (non-trak related)
135 * to be able to recover the moov structure after a crash.
137 * Currently, it writes the MVHD atom.
140 atoms_recov_write_moov_info (FILE * f, AtomMOOV * moov)
145 guint64 atom_size = 0;
150 data = g_malloc (size);
151 atom_size = atom_mvhd_copy_data (&moov->mvhd, &data, &size, &offset);
153 writen = fwrite (data, 1, atom_size, f);
155 return atom_size > 0 && writen == atom_size;
159 * Writes the number of traks to the file.
160 * This simply writes a guint32 in BE.
163 atoms_recov_write_traks_number (FILE * f, guint32 traks)
166 GST_WRITE_UINT32_BE (data, traks);
167 return fwrite (data, 4, 1, f) == 1;
171 * Writes the moov's timescale to the file
172 * This simply writes a guint32 in BE.
175 atoms_recov_write_moov_timescale (FILE * f, guint32 timescale)
178 GST_WRITE_UINT32_BE (data, timescale);
179 return fwrite (data, 4, 1, f) == 1;
183 * Writes the trak atom to the file.
186 atoms_recov_write_trak_info (FILE * f, AtomTRAK * trak)
191 guint64 atom_size = 0;
194 /* buffer is realloced to a larger size if needed */
196 data = g_malloc (size);
197 atom_size = atom_trak_copy_data (trak, &data, &size, &offset);
199 writen = fwrite (data, atom_size, 1, f);
201 return atom_size > 0 && writen == atom_size;
205 atoms_recov_write_trak_samples (FILE * f, AtomTRAK * trak, guint32 nsamples,
206 guint32 delta, guint32 size, guint64 chunk_offset, gboolean sync,
207 gboolean do_pts, gint64 pts_offset)
209 guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE];
211 * We have to write a TrakBufferEntryInfo
213 GST_WRITE_UINT32_BE (data + 0, trak->tkhd.track_ID);
214 GST_WRITE_UINT32_BE (data + 4, nsamples);
215 GST_WRITE_UINT32_BE (data + 8, delta);
216 GST_WRITE_UINT32_BE (data + 12, size);
217 GST_WRITE_UINT64_BE (data + 16, chunk_offset);
219 GST_WRITE_UINT8 (data + 24, 1);
221 GST_WRITE_UINT8 (data + 24, 0);
223 GST_WRITE_UINT8 (data + 25, 1);
224 GST_WRITE_UINT64_BE (data + 26, pts_offset);
226 GST_WRITE_UINT8 (data + 25, 0);
227 GST_WRITE_UINT64_BE (data + 26, 0);
230 return fwrite (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, f) ==
231 TRAK_BUFFER_ENTRY_INFO_SIZE;
235 atoms_recov_write_headers (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix,
236 AtomMOOV * moov, guint32 timescale, guint32 traks_number)
238 if (!atoms_recov_write_version (f)) {
242 if (!atoms_recov_write_ftyp_info (f, ftyp, prefix)) {
246 if (!atoms_recov_write_moov_info (f, moov)) {
250 if (!atoms_recov_write_moov_timescale (f, timescale)) {
254 if (!atoms_recov_write_traks_number (f, traks_number)) {
262 read_atom_header (FILE * f, guint32 * fourcc, guint32 * size)
266 if (fread (aux, 1, 8, f) != 8)
268 *size = GST_READ_UINT32_BE (aux);
269 *fourcc = GST_READ_UINT32_LE (aux + 4);
274 moov_recov_file_parse_prefix (MoovRecovFile * moovrf)
278 guint32 total_size = 0;
279 if (fseek (moovrf->file, 2, SEEK_SET) != 0)
281 if (!read_atom_header (moovrf->file, &fourcc, &size)) {
285 if (fourcc != FOURCC_ftyp) {
286 /* we might have a prefix here */
287 if (fseek (moovrf->file, size - 8, SEEK_CUR) != 0)
292 /* now read the ftyp */
293 if (!read_atom_header (moovrf->file, &fourcc, &size))
297 /* this has to be the ftyp */
298 if (fourcc != FOURCC_ftyp)
301 moovrf->prefix_size = total_size;
302 return fseek (moovrf->file, size - 8, SEEK_CUR) == 0;
306 moov_recov_file_parse_mvhd (MoovRecovFile * moovrf)
310 if (!read_atom_header (moovrf->file, &fourcc, &size)) {
313 /* check for sanity */
314 if (fourcc != FOURCC_mvhd)
317 moovrf->mvhd_size = size;
318 moovrf->mvhd_pos = ftell (moovrf->file) - 8;
320 /* skip the remaining of the mvhd in the file */
321 return fseek (moovrf->file, size - 8, SEEK_CUR) == 0;
325 mdat_recov_file_parse_mdat_start (MdatRecovFile * mdatrf)
327 guint32 fourcc, size;
329 if (!read_atom_header (mdatrf->file, &fourcc, &size)) {
333 mdatrf->mdat_header_size = 16;
334 mdatrf->mdat_size = 16;
336 mdatrf->mdat_header_size = 8;
337 mdatrf->mdat_size = 8;
339 mdatrf->mdat_start = ftell (mdatrf->file) - 8;
341 return fourcc == FOURCC_mdat;
345 mdat_recov_file_find_mdat (FILE * file, GError ** err)
347 guint32 fourcc = 0, size = 0;
348 gboolean failure = FALSE;
349 while (fourcc != FOURCC_mdat && !failure) {
350 if (!read_atom_header (file, &fourcc, &size)) {
354 /* skip these atoms */
358 if (fseek (file, size - 8, SEEK_CUR) != 0) {
359 goto file_seek_error;
365 GST_ERROR ("Unexpected atom in headers %" GST_FOURCC_FORMAT,
366 GST_FOURCC_ARGS (fourcc));
373 /* Reverse to mdat start */
374 if (fseek (file, -8, SEEK_CUR) != 0)
375 goto file_seek_error;
381 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
382 "Failed to parse atom");
386 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
387 "Failed to seek to start of the file");
393 mdat_recov_file_create (FILE * file, gboolean datafile, GError ** err)
395 MdatRecovFile *mrf = g_new0 (MdatRecovFile, 1);
397 g_return_val_if_fail (file != NULL, NULL);
400 mrf->rawfile = datafile;
402 /* get the file/data length */
403 if (fseek (file, 0, SEEK_END) != 0)
404 goto file_length_error;
405 /* still needs to deduce the mdat header and ftyp size */
406 mrf->data_size = ftell (file);
407 if (mrf->data_size == -1L)
408 goto file_length_error;
410 if (fseek (file, 0, SEEK_SET) != 0)
411 goto file_seek_error;
414 /* this file contains no atoms, only raw data to be placed on the mdat
415 * this happens when faststart mode is used */
417 mrf->mdat_header_size = 16;
422 if (!mdat_recov_file_find_mdat (file, err)) {
426 /* we don't parse this if we have a tmpdatafile */
427 if (!mdat_recov_file_parse_mdat_start (mrf)) {
428 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
429 "Error while parsing mdat atom");
436 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
437 "Failed to seek to start of the file");
441 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
442 "Failed to determine file size");
446 mdat_recov_file_free (mrf);
451 mdat_recov_file_free (MdatRecovFile * mrf)
458 moov_recov_parse_num_traks (MoovRecovFile * moovrf)
461 if (fread (traks, 1, 4, moovrf->file) != 4)
463 moovrf->num_traks = GST_READ_UINT32_BE (traks);
468 moov_recov_parse_moov_timescale (MoovRecovFile * moovrf)
471 if (fread (ts, 1, 4, moovrf->file) != 4)
473 moovrf->timescale = GST_READ_UINT32_BE (ts);
478 skip_atom (MoovRecovFile * moovrf, guint32 expected_fourcc)
483 if (!read_atom_header (moovrf->file, &fourcc, &size))
485 if (fourcc != expected_fourcc)
488 return (fseek (moovrf->file, size - 8, SEEK_CUR) == 0);
492 moov_recov_parse_tkhd (MoovRecovFile * moovrf, TrakRecovData * trakrd)
498 /* make sure we are on a tkhd atom */
499 if (!read_atom_header (moovrf->file, &fourcc, &size))
501 if (fourcc != FOURCC_tkhd)
504 trakrd->tkhd_file_offset = ftell (moovrf->file) - 8;
506 /* move 8 bytes forward to the trak_id pos */
507 if (fseek (moovrf->file, 12, SEEK_CUR) != 0)
509 if (fread (data, 1, 4, moovrf->file) != 4)
512 /* advance the rest of tkhd */
513 if (fseek (moovrf->file, 68, SEEK_CUR) != 0)
516 trakrd->trak_id = GST_READ_UINT32_BE (data);
521 moov_recov_parse_stbl (MoovRecovFile * moovrf, TrakRecovData * trakrd)
527 if (!read_atom_header (moovrf->file, &fourcc, &size))
529 if (fourcc != FOURCC_stbl)
532 trakrd->stbl_file_offset = ftell (moovrf->file) - 8;
533 trakrd->stbl_size = size;
536 if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
538 if (fourcc != FOURCC_stsd)
540 if (fseek (moovrf->file, auxsize - 8, SEEK_CUR) != 0)
543 trakrd->stsd_size = auxsize;
544 trakrd->post_stsd_offset = ftell (moovrf->file);
546 /* as this is the last atom we parse, we don't skip forward */
552 moov_recov_parse_minf (MoovRecovFile * moovrf, TrakRecovData * trakrd)
558 if (!read_atom_header (moovrf->file, &fourcc, &size))
560 if (fourcc != FOURCC_minf)
563 trakrd->minf_file_offset = ftell (moovrf->file) - 8;
564 trakrd->minf_size = size;
566 /* skip either of vmhd, smhd, hmhd that might follow */
567 if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
569 if (fourcc != FOURCC_vmhd && fourcc != FOURCC_smhd && fourcc != FOURCC_hmhd &&
570 fourcc != FOURCC_gmhd)
572 if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
575 /* skip a possible hdlr and the following dinf */
576 if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
578 if (fourcc == FOURCC_hdlr) {
579 if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
581 if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
584 if (fourcc != FOURCC_dinf)
586 if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
589 /* now we are ready to read the stbl */
590 if (!moov_recov_parse_stbl (moovrf, trakrd))
597 moov_recov_parse_mdhd (MoovRecovFile * moovrf, TrakRecovData * trakrd)
603 /* make sure we are on a tkhd atom */
604 if (!read_atom_header (moovrf->file, &fourcc, &size))
606 if (fourcc != FOURCC_mdhd)
609 trakrd->mdhd_file_offset = ftell (moovrf->file) - 8;
611 /* get the timescale */
612 if (fseek (moovrf->file, 12, SEEK_CUR) != 0)
614 if (fread (data, 1, 4, moovrf->file) != 4)
616 trakrd->timescale = GST_READ_UINT32_BE (data);
617 if (fseek (moovrf->file, 8, SEEK_CUR) != 0)
623 moov_recov_parse_mdia (MoovRecovFile * moovrf, TrakRecovData * trakrd)
628 /* make sure we are on a tkhd atom */
629 if (!read_atom_header (moovrf->file, &fourcc, &size))
631 if (fourcc != FOURCC_mdia)
634 trakrd->mdia_file_offset = ftell (moovrf->file) - 8;
635 trakrd->mdia_size = size;
637 if (!moov_recov_parse_mdhd (moovrf, trakrd))
640 if (!skip_atom (moovrf, FOURCC_hdlr))
642 if (!moov_recov_parse_minf (moovrf, trakrd))
648 moov_recov_parse_trak (MoovRecovFile * moovrf, TrakRecovData * trakrd)
654 offset = ftell (moovrf->file);
659 /* make sure we are on a trak atom */
660 if (!read_atom_header (moovrf->file, &fourcc, &size)) {
663 if (fourcc != FOURCC_trak) {
666 trakrd->trak_size = size;
668 /* now we should have a trak header 'tkhd' */
669 if (!moov_recov_parse_tkhd (moovrf, trakrd))
672 /* FIXME add edts handling here and in qtmux, as this is only detected
673 * after buffers start flowing */
675 if (!moov_recov_parse_mdia (moovrf, trakrd))
678 if (fseek (moovrf->file,
679 (long int) trakrd->mdia_file_offset + trakrd->mdia_size,
683 trakrd->extra_atoms_offset = ftell (moovrf->file);
684 trakrd->extra_atoms_size = size - (trakrd->extra_atoms_offset - offset);
686 trakrd->file_offset = offset;
687 /* position after the trak */
688 return fseek (moovrf->file, (long int) offset + size, SEEK_SET) == 0;
692 moov_recov_file_create (FILE * file, GError ** err)
695 MoovRecovFile *moovrf = g_new0 (MoovRecovFile, 1);
697 g_return_val_if_fail (file != NULL, NULL);
701 /* look for ftyp and prefix at the start */
702 if (!moov_recov_file_parse_prefix (moovrf)) {
703 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
704 "Error while parsing prefix atoms");
709 if (!moov_recov_file_parse_mvhd (moovrf)) {
710 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
711 "Error while parsing mvhd atom");
715 if (!moov_recov_parse_moov_timescale (moovrf)) {
716 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
717 "Error while parsing timescale");
720 if (!moov_recov_parse_num_traks (moovrf)) {
721 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
722 "Error while parsing parsing number of traks");
727 if (moovrf->num_traks > 1024) {
728 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
729 "Unsupported number of traks");
734 moovrf->traks_rd = g_new0 (TrakRecovData, moovrf->num_traks);
735 for (i = 0; i < moovrf->num_traks; i++) {
736 atom_stbl_init (&(moovrf->traks_rd[i].stbl));
738 for (i = 0; i < moovrf->num_traks; i++) {
739 if (!moov_recov_parse_trak (moovrf, &(moovrf->traks_rd[i]))) {
740 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
741 "Error while parsing trak atom");
749 moov_recov_file_free (moovrf);
754 moov_recov_file_free (MoovRecovFile * moovrf)
757 fclose (moovrf->file);
758 if (moovrf->traks_rd) {
759 for (i = 0; i < moovrf->num_traks; i++) {
760 atom_stbl_clear (&(moovrf->traks_rd[i].stbl));
762 g_free (moovrf->traks_rd);
768 moov_recov_parse_buffer_entry (MoovRecovFile * moovrf, TrakBufferEntryInfo * b)
770 guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE];
773 read = fread (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, moovrf->file);
774 if (read != TRAK_BUFFER_ENTRY_INFO_SIZE)
777 b->track_id = GST_READ_UINT32_BE (data);
778 b->nsamples = GST_READ_UINT32_BE (data + 4);
779 b->delta = GST_READ_UINT32_BE (data + 8);
780 b->size = GST_READ_UINT32_BE (data + 12);
781 b->chunk_offset = GST_READ_UINT64_BE (data + 16);
782 b->sync = data[24] != 0;
783 b->do_pts = data[25] != 0;
784 b->pts_offset = GST_READ_UINT64_BE (data + 26);
789 mdat_recov_add_sample (MdatRecovFile * mdatrf, guint32 size)
791 /* test if this data exists */
792 if (mdatrf->mdat_size - mdatrf->mdat_header_size + size > mdatrf->data_size)
795 mdatrf->mdat_size += size;
799 static TrakRecovData *
800 moov_recov_get_trak (MoovRecovFile * moovrf, guint32 id)
803 for (i = 0; i < moovrf->num_traks; i++) {
804 if (moovrf->traks_rd[i].trak_id == id)
805 return &(moovrf->traks_rd[i]);
811 trak_recov_data_add_sample (TrakRecovData * trak, TrakBufferEntryInfo * b)
813 trak->duration += b->nsamples * b->delta;
814 atom_stbl_add_samples (&trak->stbl, b->nsamples, b->delta, b->size,
815 b->chunk_offset, b->sync, b->pts_offset);
819 * Parses the buffer entries in the MoovRecovFile and matches the inputs
820 * with the data in the MdatRecovFile. Whenever a buffer entry of that
821 * represents 'x' bytes of data, the same amount of data is 'validated' in
822 * the MdatRecovFile and will be inluded in the generated moovie file.
825 moov_recov_parse_buffers (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
828 TrakBufferEntryInfo entry;
831 /* we assume both moovrf and mdatrf are at the starting points of their
833 while (moov_recov_parse_buffer_entry (moovrf, &entry)) {
834 /* be sure we still have this data in mdat */
835 trak = moov_recov_get_trak (moovrf, entry.track_id);
837 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
838 "Invalid trak id found in buffer entry");
841 if (!mdat_recov_add_sample (mdatrf, entry.size))
843 trak_recov_data_add_sample (trak, &entry);
849 trak_recov_data_get_trak_atom_size (TrakRecovData * trak)
851 AtomSTBL *stbl = &trak->stbl;
854 /* write out our stbl child atoms */
857 if (!atom_stts_copy_data (&stbl->stts, NULL, NULL, &offset)) {
860 if (atom_array_get_len (&stbl->stss.entries) > 0) {
861 if (!atom_stss_copy_data (&stbl->stss, NULL, NULL, &offset)) {
865 if (!atom_stsc_copy_data (&stbl->stsc, NULL, NULL, &offset)) {
868 if (!atom_stsz_copy_data (&stbl->stsz, NULL, NULL, &offset)) {
872 if (!atom_ctts_copy_data (stbl->ctts, NULL, NULL, &offset)) {
876 if (!atom_stco64_copy_data (&stbl->stco64, NULL, NULL, &offset)) {
880 return trak->trak_size + ((trak->stsd_size + offset + 8) - trak->stbl_size);
887 moov_recov_get_stbl_children_data (MoovRecovFile * moovrf, TrakRecovData * trak,
890 AtomSTBL *stbl = &trak->stbl;
895 /* write out our stbl child atoms
897 * Use 1MB as a starting size, *_copy_data functions
898 * will grow the buffer if needed.
901 buffer = g_malloc0 (size);
904 if (!atom_stts_copy_data (&stbl->stts, &buffer, &size, &offset)) {
907 if (atom_array_get_len (&stbl->stss.entries) > 0) {
908 if (!atom_stss_copy_data (&stbl->stss, &buffer, &size, &offset)) {
912 if (!atom_stsc_copy_data (&stbl->stsc, &buffer, &size, &offset)) {
915 if (!atom_stsz_copy_data (&stbl->stsz, &buffer, &size, &offset)) {
919 if (!atom_ctts_copy_data (stbl->ctts, &buffer, &size, &offset)) {
923 if (!atom_stco64_copy_data (&stbl->stco64, &buffer, &size, &offset)) {
935 copy_data_from_file_to_file (FILE * from, guint position, guint size, FILE * to,
940 if (fseek (from, position, SEEK_SET) != 0)
942 data = g_malloc (size);
943 if (fread (data, 1, size, from) != size) {
946 if (fwrite (data, 1, size, to) != size) {
947 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
960 moov_recov_write_file (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
961 FILE * outf, GError ** err, GError ** warn)
965 guint8 *prefix_data = NULL;
966 guint8 *mvhd_data = NULL;
967 guint8 *trak_data = NULL;
968 guint32 moov_size = 0;
970 guint64 stbl_children_size = 0;
971 guint8 *stbl_children = NULL;
972 guint32 longest_duration = 0;
976 /* check the version */
977 if (fseek (moovrf->file, 0, SEEK_SET) != 0) {
978 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
979 "Failed to seek to the start of the moov recovery file");
982 if (fread (auxdata, 1, 2, moovrf->file) != 2) {
983 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
984 "Failed to read version from file");
987 version = GST_READ_UINT16_BE (auxdata);
988 if (version != ATOMS_RECOV_FILE_VERSION) {
989 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_VERSION,
990 "Input file version (%u) is not supported in this version (%u)",
991 version, ATOMS_RECOV_FILE_VERSION);
996 prefix_data = g_malloc (moovrf->prefix_size);
997 if (fread (prefix_data, 1, moovrf->prefix_size,
998 moovrf->file) != moovrf->prefix_size) {
999 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
1000 "Failed to read the ftyp atom from file");
1003 if (fwrite (prefix_data, 1, moovrf->prefix_size, outf) != moovrf->prefix_size) {
1004 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1007 g_free (prefix_data);
1010 /* need to calculate the moov size beforehand to add the offset to
1011 * chunk offset entries */
1012 moov_size += moovrf->mvhd_size + 8; /* mvhd + moov size + fourcc */
1013 for (i = 0; i < moovrf->num_traks; i++) {
1014 TrakRecovData *trak = &(moovrf->traks_rd[i]);
1015 guint32 duration; /* in moov's timescale */
1018 /* convert trak duration to moov's duration */
1019 duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale,
1022 if (duration > longest_duration)
1023 longest_duration = duration;
1024 trak_size = trak_recov_data_get_trak_atom_size (trak);
1025 if (trak_size == 0) {
1026 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_GENERIC,
1027 "Failed to estimate trak atom size");
1030 moov_size += trak_size;
1033 /* add chunks offsets */
1034 for (i = 0; i < moovrf->num_traks; i++) {
1035 TrakRecovData *trak = &(moovrf->traks_rd[i]);
1036 /* 8 or 16 for the mdat header */
1037 gint64 offset = moov_size + ftell (outf) + mdatrf->mdat_header_size;
1038 atom_stco64_chunks_set_offset (&trak->stbl.stco64, offset);
1041 /* write the moov */
1042 GST_WRITE_UINT32_BE (auxdata, moov_size);
1043 GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_moov);
1044 if (fwrite (auxdata, 1, 8, outf) != 8) {
1045 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1049 /* write the mvhd */
1050 mvhd_data = g_malloc (moovrf->mvhd_size);
1051 if (fseek (moovrf->file, moovrf->mvhd_pos, SEEK_SET) != 0)
1053 if (fread (mvhd_data, 1, moovrf->mvhd_size,
1054 moovrf->file) != moovrf->mvhd_size)
1056 GST_WRITE_UINT32_BE (mvhd_data + 20, moovrf->timescale);
1057 GST_WRITE_UINT32_BE (mvhd_data + 24, longest_duration);
1058 if (fwrite (mvhd_data, 1, moovrf->mvhd_size, outf) != moovrf->mvhd_size) {
1059 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1065 /* write the traks, this is the tough part because we need to update:
1067 * - sizes of atoms from stbl to trak
1070 for (i = 0; i < moovrf->num_traks; i++) {
1071 TrakRecovData *trak = &(moovrf->traks_rd[i]);
1072 guint trak_data_size;
1073 guint32 stbl_new_size;
1074 guint32 minf_new_size;
1075 guint32 mdia_new_size;
1076 guint32 trak_new_size;
1078 guint32 duration; /* in moov's timescale */
1080 /* convert trak duration to moov's duration */
1081 duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale,
1084 stbl_children = moov_recov_get_stbl_children_data (moovrf, trak,
1085 &stbl_children_size);
1086 if (stbl_children == NULL)
1089 /* calc the new size of the atoms from stbl to trak in the atoms tree */
1090 stbl_new_size = trak->stsd_size + stbl_children_size + 8;
1091 size_diff = stbl_new_size - trak->stbl_size;
1092 minf_new_size = trak->minf_size + size_diff;
1093 mdia_new_size = trak->mdia_size + size_diff;
1094 trak_new_size = trak->trak_size + size_diff;
1096 if (fseek (moovrf->file, trak->file_offset, SEEK_SET) != 0)
1098 trak_data_size = trak->post_stsd_offset - trak->file_offset;
1099 trak_data = g_malloc (trak_data_size);
1100 if (fread (trak_data, 1, trak_data_size, moovrf->file) != trak_data_size) {
1103 /* update the size values in those read atoms before writing */
1104 GST_WRITE_UINT32_BE (trak_data, trak_new_size);
1105 GST_WRITE_UINT32_BE (trak_data + (trak->mdia_file_offset -
1106 trak->file_offset), mdia_new_size);
1107 GST_WRITE_UINT32_BE (trak_data + (trak->minf_file_offset -
1108 trak->file_offset), minf_new_size);
1109 GST_WRITE_UINT32_BE (trak_data + (trak->stbl_file_offset -
1110 trak->file_offset), stbl_new_size);
1112 /* update duration values in tkhd and mdhd */
1113 GST_WRITE_UINT32_BE (trak_data + (trak->tkhd_file_offset -
1114 trak->file_offset) + 28, duration);
1115 GST_WRITE_UINT32_BE (trak_data + (trak->mdhd_file_offset -
1116 trak->file_offset) + 24, trak->duration);
1118 if (fwrite (trak_data, 1, trak_data_size, outf) != trak_data_size) {
1119 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1122 if (fwrite (stbl_children, 1, stbl_children_size, outf) !=
1123 stbl_children_size) {
1124 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1130 g_free (stbl_children);
1131 stbl_children = NULL;
1133 /* Copy the extra atoms after 'minf' */
1134 if (!copy_data_from_file_to_file (moovrf->file, trak->extra_atoms_offset,
1135 trak->extra_atoms_size, outf, err))
1139 /* write the mdat */
1140 /* write the header first */
1141 if (mdatrf->mdat_header_size == 16) {
1142 GST_WRITE_UINT32_BE (auxdata, 1);
1143 GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
1144 GST_WRITE_UINT64_BE (auxdata + 8, mdatrf->mdat_size);
1145 } else if (mdatrf->mdat_header_size == 8) {
1146 GST_WRITE_UINT32_BE (auxdata, mdatrf->mdat_size);
1147 GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
1149 GST_ERROR ("Unexpected atom size: %u", mdatrf->mdat_header_size);
1150 g_assert_not_reached ();
1154 if (fwrite (auxdata, 1, mdatrf->mdat_header_size,
1155 outf) != mdatrf->mdat_header_size) {
1156 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1160 /* now read the mdat data and output to the file */
1161 if (fseek (mdatrf->file, mdatrf->mdat_start +
1162 (mdatrf->rawfile ? 0 : mdatrf->mdat_header_size), SEEK_SET) != 0)
1165 remaining = mdatrf->mdat_size - mdatrf->mdat_header_size;
1166 data = g_malloc (MAX_CHUNK_SIZE);
1167 while (!feof (mdatrf->file) && remaining > 0) {
1168 gint read, write, readsize;
1170 readsize = MIN (MAX_CHUNK_SIZE, remaining);
1172 read = fread (data, 1, readsize, mdatrf->file);
1173 write = fwrite (data, 1, read, outf);
1176 if (write != read) {
1177 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
1178 "Failed to copy data to output file: %s", g_strerror (errno));
1185 g_set_error (warn, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
1186 "Samples in recovery file were not present on headers."
1187 " Bytes lost: %u", remaining);
1188 } else if (!feof (mdatrf->file)) {
1189 g_set_error (warn, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
1190 "Samples in headers were not found in data file.");
1191 GST_FIXME ("Rewrite mdat size if we reach this to make the file"
1198 g_free (stbl_children);
1200 g_free (prefix_data);