"Fossies" - the Fresh Open Source Software Archive 
Member "gst-plugins-good-1.20.3/gst/isomp4/atomsrecovery.c" (15 Jun 2022, 33318 Bytes) of package /linux/misc/gst-plugins-good-1.20.3.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "atomsrecovery.c" see the
Fossies "Dox" file reference documentation.
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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, 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-1.0 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 MAX_CHUNK_SIZE (1024 * 1024) /* 1MB */
92
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))
96
97 static gboolean
98 atoms_recov_write_version (FILE * f)
99 {
100 guint8 data[2];
101 GST_WRITE_UINT16_BE (data, ATOMS_RECOV_FILE_VERSION);
102 return fwrite (data, 2, 1, f) == 1;
103 }
104
105 static gboolean
106 atoms_recov_write_ftyp_info (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix)
107 {
108 guint8 *data = NULL;
109 guint64 offset = 0;
110 guint64 size = 0;
111
112 if (prefix) {
113 GstMapInfo map;
114
115 if (!gst_buffer_map (prefix, &map, GST_MAP_READ)) {
116 return FALSE;
117 }
118 if (fwrite (map.data, 1, map.size, f) != map.size) {
119 gst_buffer_unmap (prefix, &map);
120 return FALSE;
121 }
122 gst_buffer_unmap (prefix, &map);
123 }
124 if (!atom_ftyp_copy_data (ftyp, &data, &size, &offset)) {
125 return FALSE;
126 }
127 if (fwrite (data, 1, offset, f) != offset) {
128 g_free (data);
129 return FALSE;
130 }
131 g_free (data);
132 return TRUE;
133 }
134
135 /*
136 * Writes important info on the 'moov' atom (non-trak related)
137 * to be able to recover the moov structure after a crash.
138 *
139 * Currently, it writes the MVHD atom.
140 */
141 static gboolean
142 atoms_recov_write_moov_info (FILE * f, AtomMOOV * moov)
143 {
144 guint8 *data;
145 guint64 size;
146 guint64 offset = 0;
147 guint64 atom_size = 0;
148 gint writen = 0;
149
150 /* likely enough */
151 size = 256;
152 data = g_malloc (size);
153 atom_size = atom_mvhd_copy_data (&moov->mvhd, &data, &size, &offset);
154 if (atom_size > 0)
155 writen = fwrite (data, 1, atom_size, f);
156 g_free (data);
157 return atom_size > 0 && writen == atom_size;
158 }
159
160 /*
161 * Writes the number of traks to the file.
162 * This simply writes a guint32 in BE.
163 */
164 static gboolean
165 atoms_recov_write_traks_number (FILE * f, guint32 traks)
166 {
167 guint8 data[4];
168 GST_WRITE_UINT32_BE (data, traks);
169 return fwrite (data, 4, 1, f) == 1;
170 }
171
172 /*
173 * Writes the moov's timescale to the file
174 * This simply writes a guint32 in BE.
175 */
176 static gboolean
177 atoms_recov_write_moov_timescale (FILE * f, guint32 timescale)
178 {
179 guint8 data[4];
180 GST_WRITE_UINT32_BE (data, timescale);
181 return fwrite (data, 4, 1, f) == 1;
182 }
183
184 /*
185 * Writes the trak atom to the file.
186 */
187 gboolean
188 atoms_recov_write_trak_info (FILE * f, AtomTRAK * trak)
189 {
190 guint8 *data;
191 guint64 size;
192 guint64 offset = 0;
193 guint64 atom_size = 0;
194 gint writen = 0;
195
196 /* buffer is realloced to a larger size if needed */
197 size = 4 * 1024;
198 data = g_malloc (size);
199 atom_size = atom_trak_copy_data (trak, &data, &size, &offset);
200 if (atom_size > 0)
201 writen = fwrite (data, atom_size, 1, f);
202 g_free (data);
203 return atom_size > 0 && writen == atom_size;
204 }
205
206 gboolean
207 atoms_recov_write_trak_samples (FILE * f, AtomTRAK * trak, guint32 nsamples,
208 guint32 delta, guint32 size, guint64 chunk_offset, gboolean sync,
209 gboolean do_pts, gint64 pts_offset)
210 {
211 guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE];
212 /*
213 * We have to write a TrakBufferEntryInfo
214 */
215 GST_WRITE_UINT32_BE (data + 0, trak->tkhd.track_ID);
216 GST_WRITE_UINT32_BE (data + 4, nsamples);
217 GST_WRITE_UINT32_BE (data + 8, delta);
218 GST_WRITE_UINT32_BE (data + 12, size);
219 GST_WRITE_UINT64_BE (data + 16, chunk_offset);
220 if (sync)
221 GST_WRITE_UINT8 (data + 24, 1);
222 else
223 GST_WRITE_UINT8 (data + 24, 0);
224 if (do_pts) {
225 GST_WRITE_UINT8 (data + 25, 1);
226 GST_WRITE_UINT64_BE (data + 26, pts_offset);
227 } else {
228 GST_WRITE_UINT8 (data + 25, 0);
229 GST_WRITE_UINT64_BE (data + 26, 0);
230 }
231
232 return fwrite (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, f) ==
233 TRAK_BUFFER_ENTRY_INFO_SIZE;
234 }
235
236 gboolean
237 atoms_recov_write_headers (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix,
238 AtomMOOV * moov, guint32 timescale, guint32 traks_number)
239 {
240 if (!atoms_recov_write_version (f)) {
241 return FALSE;
242 }
243
244 if (!atoms_recov_write_ftyp_info (f, ftyp, prefix)) {
245 return FALSE;
246 }
247
248 if (!atoms_recov_write_moov_info (f, moov)) {
249 return FALSE;
250 }
251
252 if (!atoms_recov_write_moov_timescale (f, timescale)) {
253 return FALSE;
254 }
255
256 if (!atoms_recov_write_traks_number (f, traks_number)) {
257 return FALSE;
258 }
259
260 return TRUE;
261 }
262
263 static gboolean
264 read_atom_header (FILE * f, guint32 * fourcc, guint32 * size)
265 {
266 guint8 aux[8];
267
268 if (fread (aux, 1, 8, f) != 8)
269 return FALSE;
270 *size = GST_READ_UINT32_BE (aux);
271 *fourcc = GST_READ_UINT32_LE (aux + 4);
272 return TRUE;
273 }
274
275 static gboolean
276 moov_recov_file_parse_prefix (MoovRecovFile * moovrf)
277 {
278 guint32 fourcc;
279 guint32 size;
280 guint32 total_size = 0;
281 if (fseek (moovrf->file, 2, SEEK_SET) != 0)
282 return FALSE;
283 if (!read_atom_header (moovrf->file, &fourcc, &size)) {
284 return FALSE;
285 }
286
287 if (fourcc != FOURCC_ftyp) {
288 /* we might have a prefix here */
289 if (fseek (moovrf->file, size - 8, SEEK_CUR) != 0)
290 return FALSE;
291
292 total_size += size;
293
294 /* now read the ftyp */
295 if (!read_atom_header (moovrf->file, &fourcc, &size))
296 return FALSE;
297 }
298
299 /* this has to be the ftyp */
300 if (fourcc != FOURCC_ftyp)
301 return FALSE;
302 total_size += size;
303 moovrf->prefix_size = total_size;
304 return fseek (moovrf->file, size - 8, SEEK_CUR) == 0;
305 }
306
307 static gboolean
308 moov_recov_file_parse_mvhd (MoovRecovFile * moovrf)
309 {
310 guint32 fourcc;
311 guint32 size;
312 if (!read_atom_header (moovrf->file, &fourcc, &size)) {
313 return FALSE;
314 }
315 /* check for sanity */
316 if (fourcc != FOURCC_mvhd)
317 return FALSE;
318
319 moovrf->mvhd_size = size;
320 moovrf->mvhd_pos = ftell (moovrf->file) - 8;
321
322 /* skip the remaining of the mvhd in the file */
323 return fseek (moovrf->file, size - 8, SEEK_CUR) == 0;
324 }
325
326 static gboolean
327 mdat_recov_file_parse_mdat_start (MdatRecovFile * mdatrf)
328 {
329 guint32 fourcc, size;
330
331 if (!read_atom_header (mdatrf->file, &fourcc, &size)) {
332 return FALSE;
333 }
334 if (size == 1) {
335 mdatrf->mdat_header_size = 16;
336 mdatrf->mdat_size = 16;
337 } else {
338 mdatrf->mdat_header_size = 8;
339 mdatrf->mdat_size = 8;
340 }
341 mdatrf->mdat_start = ftell (mdatrf->file) - 8;
342
343 return fourcc == FOURCC_mdat;
344 }
345
346 static gboolean
347 mdat_recov_file_find_mdat (FILE * file, GError ** err)
348 {
349 guint32 fourcc = 0, size = 0;
350 gboolean failure = FALSE;
351 while (fourcc != FOURCC_mdat && !failure) {
352 if (!read_atom_header (file, &fourcc, &size)) {
353 goto parse_error;
354 }
355 switch (fourcc) {
356 /* skip these atoms */
357 case FOURCC_ftyp:
358 case FOURCC_free:
359 case FOURCC_udta:
360 if (fseek (file, size - 8, SEEK_CUR) != 0) {
361 goto file_seek_error;
362 }
363 break;
364 case FOURCC_mdat:
365 break;
366 default:
367 GST_ERROR ("Unexpected atom in headers %" GST_FOURCC_FORMAT,
368 GST_FOURCC_ARGS (fourcc));
369 failure = TRUE;
370 break;
371 }
372 }
373
374 if (!failure) {
375 /* Reverse to mdat start */
376 if (fseek (file, -8, SEEK_CUR) != 0)
377 goto file_seek_error;
378 }
379
380 return !failure;
381
382 parse_error:
383 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
384 "Failed to parse atom");
385 return FALSE;
386
387 file_seek_error:
388 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
389 "Failed to seek to start of the file");
390 return FALSE;
391
392 }
393
394 MdatRecovFile *
395 mdat_recov_file_create (FILE * file, gboolean datafile, GError ** err)
396 {
397 MdatRecovFile *mrf = g_new0 (MdatRecovFile, 1);
398
399 g_return_val_if_fail (file != NULL, NULL);
400
401 mrf->file = file;
402 mrf->rawfile = datafile;
403
404 /* get the file/data length */
405 if (fseek (file, 0, SEEK_END) != 0)
406 goto file_length_error;
407 /* still needs to deduce the mdat header and ftyp size */
408 mrf->data_size = ftell (file);
409 if (mrf->data_size == -1L)
410 goto file_length_error;
411
412 if (fseek (file, 0, SEEK_SET) != 0)
413 goto file_seek_error;
414
415 if (datafile) {
416 /* this file contains no atoms, only raw data to be placed on the mdat
417 * this happens when faststart mode is used */
418 mrf->mdat_start = 0;
419 mrf->mdat_header_size = 16;
420 mrf->mdat_size = 16;
421 return mrf;
422 }
423
424 if (!mdat_recov_file_find_mdat (file, err)) {
425 goto fail;
426 }
427
428 /* we don't parse this if we have a tmpdatafile */
429 if (!mdat_recov_file_parse_mdat_start (mrf)) {
430 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
431 "Error while parsing mdat atom");
432 goto fail;
433 }
434
435 return mrf;
436
437 file_seek_error:
438 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
439 "Failed to seek to start of the file");
440 goto fail;
441
442 file_length_error:
443 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
444 "Failed to determine file size");
445 goto fail;
446
447 fail:
448 mdat_recov_file_free (mrf);
449 return NULL;
450 }
451
452 void
453 mdat_recov_file_free (MdatRecovFile * mrf)
454 {
455 fclose (mrf->file);
456 g_free (mrf);
457 }
458
459 static gboolean
460 moov_recov_parse_num_traks (MoovRecovFile * moovrf)
461 {
462 guint8 traks[4];
463 if (fread (traks, 1, 4, moovrf->file) != 4)
464 return FALSE;
465 moovrf->num_traks = GST_READ_UINT32_BE (traks);
466 return TRUE;
467 }
468
469 static gboolean
470 moov_recov_parse_moov_timescale (MoovRecovFile * moovrf)
471 {
472 guint8 ts[4];
473 if (fread (ts, 1, 4, moovrf->file) != 4)
474 return FALSE;
475 moovrf->timescale = GST_READ_UINT32_BE (ts);
476 return TRUE;
477 }
478
479 static gboolean
480 skip_atom (MoovRecovFile * moovrf, guint32 expected_fourcc)
481 {
482 guint32 size;
483 guint32 fourcc;
484
485 if (!read_atom_header (moovrf->file, &fourcc, &size))
486 return FALSE;
487 if (fourcc != expected_fourcc)
488 return FALSE;
489
490 return (fseek (moovrf->file, size - 8, SEEK_CUR) == 0);
491 }
492
493 static gboolean
494 moov_recov_parse_tkhd (MoovRecovFile * moovrf, TrakRecovData * trakrd)
495 {
496 guint32 size;
497 guint32 fourcc;
498 guint8 data[4];
499
500 /* make sure we are on a tkhd atom */
501 if (!read_atom_header (moovrf->file, &fourcc, &size))
502 return FALSE;
503 if (fourcc != FOURCC_tkhd)
504 return FALSE;
505
506 trakrd->tkhd_file_offset = ftell (moovrf->file) - 8;
507
508 /* move 8 bytes forward to the trak_id pos */
509 if (fseek (moovrf->file, 12, SEEK_CUR) != 0)
510 return FALSE;
511 if (fread (data, 1, 4, moovrf->file) != 4)
512 return FALSE;
513
514 /* advance the rest of tkhd */
515 if (fseek (moovrf->file, 68, SEEK_CUR) != 0)
516 return FALSE;
517
518 trakrd->trak_id = GST_READ_UINT32_BE (data);
519 return TRUE;
520 }
521
522 static gboolean
523 moov_recov_parse_stbl (MoovRecovFile * moovrf, TrakRecovData * trakrd)
524 {
525 guint32 size;
526 guint32 fourcc;
527 guint32 auxsize;
528
529 if (!read_atom_header (moovrf->file, &fourcc, &size))
530 return FALSE;
531 if (fourcc != FOURCC_stbl)
532 return FALSE;
533
534 trakrd->stbl_file_offset = ftell (moovrf->file) - 8;
535 trakrd->stbl_size = size;
536
537 /* skip the stsd */
538 if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
539 return FALSE;
540 if (fourcc != FOURCC_stsd)
541 return FALSE;
542 if (fseek (moovrf->file, auxsize - 8, SEEK_CUR) != 0)
543 return FALSE;
544
545 trakrd->stsd_size = auxsize;
546 trakrd->post_stsd_offset = ftell (moovrf->file);
547
548 /* as this is the last atom we parse, we don't skip forward */
549
550 return TRUE;
551 }
552
553 static gboolean
554 moov_recov_parse_minf (MoovRecovFile * moovrf, TrakRecovData * trakrd)
555 {
556 guint32 size;
557 guint32 fourcc;
558 guint32 auxsize;
559
560 if (!read_atom_header (moovrf->file, &fourcc, &size))
561 return FALSE;
562 if (fourcc != FOURCC_minf)
563 return FALSE;
564
565 trakrd->minf_file_offset = ftell (moovrf->file) - 8;
566 trakrd->minf_size = size;
567
568 /* skip either of vmhd, smhd, hmhd that might follow */
569 if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
570 return FALSE;
571 if (fourcc != FOURCC_vmhd && fourcc != FOURCC_smhd && fourcc != FOURCC_hmhd &&
572 fourcc != FOURCC_gmhd)
573 return FALSE;
574 if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
575 return FALSE;
576
577 /* skip a possible hdlr and the following dinf */
578 if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
579 return FALSE;
580 if (fourcc == FOURCC_hdlr) {
581 if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
582 return FALSE;
583 if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
584 return FALSE;
585 }
586 if (fourcc != FOURCC_dinf)
587 return FALSE;
588 if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
589 return FALSE;
590
591 /* now we are ready to read the stbl */
592 if (!moov_recov_parse_stbl (moovrf, trakrd))
593 return FALSE;
594
595 return TRUE;
596 }
597
598 static gboolean
599 moov_recov_parse_mdhd (MoovRecovFile * moovrf, TrakRecovData * trakrd)
600 {
601 guint32 size;
602 guint32 fourcc;
603 guint8 data[4];
604
605 /* make sure we are on a tkhd atom */
606 if (!read_atom_header (moovrf->file, &fourcc, &size))
607 return FALSE;
608 if (fourcc != FOURCC_mdhd)
609 return FALSE;
610
611 trakrd->mdhd_file_offset = ftell (moovrf->file) - 8;
612
613 /* get the timescale */
614 if (fseek (moovrf->file, 12, SEEK_CUR) != 0)
615 return FALSE;
616 if (fread (data, 1, 4, moovrf->file) != 4)
617 return FALSE;
618 trakrd->timescale = GST_READ_UINT32_BE (data);
619 if (fseek (moovrf->file, 8, SEEK_CUR) != 0)
620 return FALSE;
621 return TRUE;
622 }
623
624 static gboolean
625 moov_recov_parse_mdia (MoovRecovFile * moovrf, TrakRecovData * trakrd)
626 {
627 guint32 size;
628 guint32 fourcc;
629
630 /* make sure we are on a tkhd atom */
631 if (!read_atom_header (moovrf->file, &fourcc, &size))
632 return FALSE;
633 if (fourcc != FOURCC_mdia)
634 return FALSE;
635
636 trakrd->mdia_file_offset = ftell (moovrf->file) - 8;
637 trakrd->mdia_size = size;
638
639 if (!moov_recov_parse_mdhd (moovrf, trakrd))
640 return FALSE;
641
642 if (!skip_atom (moovrf, FOURCC_hdlr))
643 return FALSE;
644 if (!moov_recov_parse_minf (moovrf, trakrd))
645 return FALSE;
646 return TRUE;
647 }
648
649 static gboolean
650 moov_recov_parse_trak (MoovRecovFile * moovrf, TrakRecovData * trakrd)
651 {
652 guint64 offset;
653 guint32 size;
654 guint32 fourcc;
655
656 offset = ftell (moovrf->file);
657 if (offset == -1) {
658 return FALSE;
659 }
660
661 /* make sure we are on a trak atom */
662 if (!read_atom_header (moovrf->file, &fourcc, &size)) {
663 return FALSE;
664 }
665 if (fourcc != FOURCC_trak) {
666 return FALSE;
667 }
668 trakrd->trak_size = size;
669
670 /* now we should have a trak header 'tkhd' */
671 if (!moov_recov_parse_tkhd (moovrf, trakrd))
672 return FALSE;
673
674 /* FIXME add edts handling here and in qtmux, as this is only detected
675 * after buffers start flowing */
676
677 if (!moov_recov_parse_mdia (moovrf, trakrd))
678 return FALSE;
679
680 if (fseek (moovrf->file,
681 (long int) trakrd->mdia_file_offset + trakrd->mdia_size,
682 SEEK_SET) != 0)
683 return FALSE;
684
685 trakrd->extra_atoms_offset = ftell (moovrf->file);
686 trakrd->extra_atoms_size = size - (trakrd->extra_atoms_offset - offset);
687
688 trakrd->file_offset = offset;
689 /* position after the trak */
690 return fseek (moovrf->file, (long int) offset + size, SEEK_SET) == 0;
691 }
692
693 MoovRecovFile *
694 moov_recov_file_create (FILE * file, GError ** err)
695 {
696 gint i;
697 MoovRecovFile *moovrf = g_new0 (MoovRecovFile, 1);
698
699 g_return_val_if_fail (file != NULL, NULL);
700
701 moovrf->file = file;
702
703 /* look for ftyp and prefix at the start */
704 if (!moov_recov_file_parse_prefix (moovrf)) {
705 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
706 "Error while parsing prefix atoms");
707 goto fail;
708 }
709
710 /* parse the mvhd */
711 if (!moov_recov_file_parse_mvhd (moovrf)) {
712 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
713 "Error while parsing mvhd atom");
714 goto fail;
715 }
716
717 if (!moov_recov_parse_moov_timescale (moovrf)) {
718 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
719 "Error while parsing timescale");
720 goto fail;
721 }
722 if (!moov_recov_parse_num_traks (moovrf)) {
723 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
724 "Error while parsing parsing number of traks");
725 goto fail;
726 }
727
728 /* sanity check */
729 if (moovrf->num_traks > 1024) {
730 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
731 "Unsupported number of traks");
732 goto fail;
733 }
734
735 /* init the traks */
736 moovrf->traks_rd = g_new0 (TrakRecovData, moovrf->num_traks);
737 for (i = 0; i < moovrf->num_traks; i++) {
738 atom_stbl_init (&(moovrf->traks_rd[i].stbl));
739 }
740 for (i = 0; i < moovrf->num_traks; i++) {
741 if (!moov_recov_parse_trak (moovrf, &(moovrf->traks_rd[i]))) {
742 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
743 "Error while parsing trak atom");
744 goto fail;
745 }
746 }
747
748 return moovrf;
749
750 fail:
751 moov_recov_file_free (moovrf);
752 return NULL;
753 }
754
755 void
756 moov_recov_file_free (MoovRecovFile * moovrf)
757 {
758 gint i;
759 fclose (moovrf->file);
760 if (moovrf->traks_rd) {
761 for (i = 0; i < moovrf->num_traks; i++) {
762 atom_stbl_clear (&(moovrf->traks_rd[i].stbl));
763 }
764 g_free (moovrf->traks_rd);
765 }
766 g_free (moovrf);
767 }
768
769 static gboolean
770 moov_recov_parse_buffer_entry (MoovRecovFile * moovrf, TrakBufferEntryInfo * b)
771 {
772 guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE];
773 gint read;
774
775 read = fread (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, moovrf->file);
776 if (read != TRAK_BUFFER_ENTRY_INFO_SIZE)
777 return FALSE;
778
779 b->track_id = GST_READ_UINT32_BE (data);
780 b->nsamples = GST_READ_UINT32_BE (data + 4);
781 b->delta = GST_READ_UINT32_BE (data + 8);
782 b->size = GST_READ_UINT32_BE (data + 12);
783 b->chunk_offset = GST_READ_UINT64_BE (data + 16);
784 b->sync = data[24] != 0;
785 b->do_pts = data[25] != 0;
786 b->pts_offset = GST_READ_UINT64_BE (data + 26);
787 return TRUE;
788 }
789
790 static gboolean
791 mdat_recov_add_sample (MdatRecovFile * mdatrf, guint32 size)
792 {
793 /* test if this data exists */
794 if (mdatrf->mdat_size - mdatrf->mdat_header_size + size > mdatrf->data_size)
795 return FALSE;
796
797 mdatrf->mdat_size += size;
798 return TRUE;
799 }
800
801 static TrakRecovData *
802 moov_recov_get_trak (MoovRecovFile * moovrf, guint32 id)
803 {
804 gint i;
805 for (i = 0; i < moovrf->num_traks; i++) {
806 if (moovrf->traks_rd[i].trak_id == id)
807 return &(moovrf->traks_rd[i]);
808 }
809 return NULL;
810 }
811
812 static void
813 trak_recov_data_add_sample (TrakRecovData * trak, TrakBufferEntryInfo * b)
814 {
815 trak->duration += b->nsamples * b->delta;
816 atom_stbl_add_samples (&trak->stbl, b->nsamples, b->delta, b->size,
817 b->chunk_offset, b->sync, b->pts_offset);
818 }
819
820 /*
821 * Parses the buffer entries in the MoovRecovFile and matches the inputs
822 * with the data in the MdatRecovFile. Whenever a buffer entry of that
823 * represents 'x' bytes of data, the same amount of data is 'validated' in
824 * the MdatRecovFile and will be inluded in the generated moovie file.
825 */
826 gboolean
827 moov_recov_parse_buffers (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
828 GError ** err)
829 {
830 TrakBufferEntryInfo entry;
831 TrakRecovData *trak;
832
833 /* we assume both moovrf and mdatrf are at the starting points of their
834 * data reading */
835 while (moov_recov_parse_buffer_entry (moovrf, &entry)) {
836 /* be sure we still have this data in mdat */
837 trak = moov_recov_get_trak (moovrf, entry.track_id);
838 if (trak == NULL) {
839 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
840 "Invalid trak id found in buffer entry");
841 return FALSE;
842 }
843 if (!mdat_recov_add_sample (mdatrf, entry.size))
844 break;
845 trak_recov_data_add_sample (trak, &entry);
846 }
847 return TRUE;
848 }
849
850 static guint32
851 trak_recov_data_get_trak_atom_size (TrakRecovData * trak)
852 {
853 AtomSTBL *stbl = &trak->stbl;
854 guint64 offset;
855
856 /* write out our stbl child atoms */
857 offset = 0;
858
859 if (!atom_stts_copy_data (&stbl->stts, NULL, NULL, &offset)) {
860 goto fail;
861 }
862 if (atom_array_get_len (&stbl->stss.entries) > 0) {
863 if (!atom_stss_copy_data (&stbl->stss, NULL, NULL, &offset)) {
864 goto fail;
865 }
866 }
867 if (!atom_stsc_copy_data (&stbl->stsc, NULL, NULL, &offset)) {
868 goto fail;
869 }
870 if (!atom_stsz_copy_data (&stbl->stsz, NULL, NULL, &offset)) {
871 goto fail;
872 }
873 if (stbl->ctts) {
874 if (!atom_ctts_copy_data (stbl->ctts, NULL, NULL, &offset)) {
875 goto fail;
876 }
877 }
878 if (!atom_stco64_copy_data (&stbl->stco64, NULL, NULL, &offset)) {
879 goto fail;
880 }
881
882 return trak->trak_size + ((trak->stsd_size + offset + 8) - trak->stbl_size);
883
884 fail:
885 return 0;
886 }
887
888 static guint8 *
889 moov_recov_get_stbl_children_data (MoovRecovFile * moovrf, TrakRecovData * trak,
890 guint64 * p_size)
891 {
892 AtomSTBL *stbl = &trak->stbl;
893 guint8 *buffer;
894 guint64 size;
895 guint64 offset;
896
897 /* write out our stbl child atoms
898 *
899 * Use 1MB as a starting size, *_copy_data functions
900 * will grow the buffer if needed.
901 */
902 size = 1024 * 1024;
903 buffer = g_malloc0 (size);
904 offset = 0;
905
906 if (!atom_stts_copy_data (&stbl->stts, &buffer, &size, &offset)) {
907 goto fail;
908 }
909 if (atom_array_get_len (&stbl->stss.entries) > 0) {
910 if (!atom_stss_copy_data (&stbl->stss, &buffer, &size, &offset)) {
911 goto fail;
912 }
913 }
914 if (!atom_stsc_copy_data (&stbl->stsc, &buffer, &size, &offset)) {
915 goto fail;
916 }
917 if (!atom_stsz_copy_data (&stbl->stsz, &buffer, &size, &offset)) {
918 goto fail;
919 }
920 if (stbl->ctts) {
921 if (!atom_ctts_copy_data (stbl->ctts, &buffer, &size, &offset)) {
922 goto fail;
923 }
924 }
925 if (!atom_stco64_copy_data (&stbl->stco64, &buffer, &size, &offset)) {
926 goto fail;
927 }
928 *p_size = offset;
929 return buffer;
930
931 fail:
932 g_free (buffer);
933 return NULL;
934 }
935
936 static gboolean
937 copy_data_from_file_to_file (FILE * from, guint position, guint size, FILE * to,
938 GError ** err)
939 {
940 guint8 *data = NULL;
941
942 if (fseek (from, position, SEEK_SET) != 0)
943 goto fail;
944 data = g_malloc (size);
945 if (fread (data, 1, size, from) != size) {
946 goto fail;
947 }
948 if (fwrite (data, 1, size, to) != size) {
949 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
950 goto fail;
951 }
952
953 g_free (data);
954 return TRUE;
955
956 fail:
957 g_free (data);
958 return FALSE;
959 }
960
961 gboolean
962 moov_recov_write_file (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
963 FILE * outf, GError ** err, GError ** warn)
964 {
965 guint8 auxdata[16];
966 guint8 *data = NULL;
967 guint8 *prefix_data = NULL;
968 guint8 *mvhd_data = NULL;
969 guint8 *trak_data = NULL;
970 guint32 moov_size = 0;
971 gint i;
972 guint64 stbl_children_size = 0;
973 guint8 *stbl_children = NULL;
974 guint32 longest_duration = 0;
975 guint16 version;
976 guint remaining;
977
978 /* check the version */
979 if (fseek (moovrf->file, 0, SEEK_SET) != 0) {
980 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
981 "Failed to seek to the start of the moov recovery file");
982 goto fail;
983 }
984 if (fread (auxdata, 1, 2, moovrf->file) != 2) {
985 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
986 "Failed to read version from file");
987 }
988
989 version = GST_READ_UINT16_BE (auxdata);
990 if (version != ATOMS_RECOV_FILE_VERSION) {
991 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_VERSION,
992 "Input file version (%u) is not supported in this version (%u)",
993 version, ATOMS_RECOV_FILE_VERSION);
994 return FALSE;
995 }
996
997 /* write the ftyp */
998 prefix_data = g_malloc (moovrf->prefix_size);
999 if (fread (prefix_data, 1, moovrf->prefix_size,
1000 moovrf->file) != moovrf->prefix_size) {
1001 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
1002 "Failed to read the ftyp atom from file");
1003 goto fail;
1004 }
1005 if (fwrite (prefix_data, 1, moovrf->prefix_size, outf) != moovrf->prefix_size) {
1006 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1007 goto fail;
1008 }
1009 g_free (prefix_data);
1010 prefix_data = NULL;
1011
1012 /* need to calculate the moov size beforehand to add the offset to
1013 * chunk offset entries */
1014 moov_size += moovrf->mvhd_size + 8; /* mvhd + moov size + fourcc */
1015 for (i = 0; i < moovrf->num_traks; i++) {
1016 TrakRecovData *trak = &(moovrf->traks_rd[i]);
1017 guint32 duration; /* in moov's timescale */
1018 guint32 trak_size;
1019
1020 /* convert trak duration to moov's duration */
1021 duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale,
1022 trak->timescale);
1023
1024 if (duration > longest_duration)
1025 longest_duration = duration;
1026 trak_size = trak_recov_data_get_trak_atom_size (trak);
1027 if (trak_size == 0) {
1028 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_GENERIC,
1029 "Failed to estimate trak atom size");
1030 goto fail;
1031 }
1032 moov_size += trak_size;
1033 }
1034
1035 /* add chunks offsets */
1036 for (i = 0; i < moovrf->num_traks; i++) {
1037 TrakRecovData *trak = &(moovrf->traks_rd[i]);
1038 /* 8 or 16 for the mdat header */
1039 gint64 offset = moov_size + ftell (outf) + mdatrf->mdat_header_size;
1040 atom_stco64_chunks_set_offset (&trak->stbl.stco64, offset);
1041 }
1042
1043 /* write the moov */
1044 GST_WRITE_UINT32_BE (auxdata, moov_size);
1045 GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_moov);
1046 if (fwrite (auxdata, 1, 8, outf) != 8) {
1047 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1048 goto fail;
1049 }
1050
1051 /* write the mvhd */
1052 mvhd_data = g_malloc (moovrf->mvhd_size);
1053 if (fseek (moovrf->file, moovrf->mvhd_pos, SEEK_SET) != 0)
1054 goto fail;
1055 if (fread (mvhd_data, 1, moovrf->mvhd_size,
1056 moovrf->file) != moovrf->mvhd_size)
1057 goto fail;
1058 GST_WRITE_UINT32_BE (mvhd_data + 20, moovrf->timescale);
1059 GST_WRITE_UINT32_BE (mvhd_data + 24, longest_duration);
1060 if (fwrite (mvhd_data, 1, moovrf->mvhd_size, outf) != moovrf->mvhd_size) {
1061 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1062 goto fail;
1063 }
1064 g_free (mvhd_data);
1065 mvhd_data = NULL;
1066
1067 /* write the traks, this is the tough part because we need to update:
1068 * - stbl atom
1069 * - sizes of atoms from stbl to trak
1070 * - trak duration
1071 */
1072 for (i = 0; i < moovrf->num_traks; i++) {
1073 TrakRecovData *trak = &(moovrf->traks_rd[i]);
1074 guint trak_data_size;
1075 guint32 stbl_new_size;
1076 guint32 minf_new_size;
1077 guint32 mdia_new_size;
1078 guint32 trak_new_size;
1079 guint32 size_diff;
1080 guint32 duration; /* in moov's timescale */
1081
1082 /* convert trak duration to moov's duration */
1083 duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale,
1084 trak->timescale);
1085
1086 stbl_children = moov_recov_get_stbl_children_data (moovrf, trak,
1087 &stbl_children_size);
1088 if (stbl_children == NULL)
1089 goto fail;
1090
1091 /* calc the new size of the atoms from stbl to trak in the atoms tree */
1092 stbl_new_size = trak->stsd_size + stbl_children_size + 8;
1093 size_diff = stbl_new_size - trak->stbl_size;
1094 minf_new_size = trak->minf_size + size_diff;
1095 mdia_new_size = trak->mdia_size + size_diff;
1096 trak_new_size = trak->trak_size + size_diff;
1097
1098 if (fseek (moovrf->file, trak->file_offset, SEEK_SET) != 0)
1099 goto fail;
1100 trak_data_size = trak->post_stsd_offset - trak->file_offset;
1101 trak_data = g_malloc (trak_data_size);
1102 if (fread (trak_data, 1, trak_data_size, moovrf->file) != trak_data_size) {
1103 goto fail;
1104 }
1105 /* update the size values in those read atoms before writing */
1106 GST_WRITE_UINT32_BE (trak_data, trak_new_size);
1107 GST_WRITE_UINT32_BE (trak_data + (trak->mdia_file_offset -
1108 trak->file_offset), mdia_new_size);
1109 GST_WRITE_UINT32_BE (trak_data + (trak->minf_file_offset -
1110 trak->file_offset), minf_new_size);
1111 GST_WRITE_UINT32_BE (trak_data + (trak->stbl_file_offset -
1112 trak->file_offset), stbl_new_size);
1113
1114 /* update duration values in tkhd and mdhd */
1115 GST_WRITE_UINT32_BE (trak_data + (trak->tkhd_file_offset -
1116 trak->file_offset) + 28, duration);
1117 GST_WRITE_UINT32_BE (trak_data + (trak->mdhd_file_offset -
1118 trak->file_offset) + 24, trak->duration);
1119
1120 if (fwrite (trak_data, 1, trak_data_size, outf) != trak_data_size) {
1121 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1122 goto fail;
1123 }
1124 if (fwrite (stbl_children, 1, stbl_children_size, outf) !=
1125 stbl_children_size) {
1126 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1127 goto fail;
1128 }
1129
1130 g_free (trak_data);
1131 trak_data = NULL;
1132 g_free (stbl_children);
1133 stbl_children = NULL;
1134
1135 /* Copy the extra atoms after 'minf' */
1136 if (!copy_data_from_file_to_file (moovrf->file, trak->extra_atoms_offset,
1137 trak->extra_atoms_size, outf, err))
1138 goto fail;
1139 }
1140
1141 /* write the mdat */
1142 /* write the header first */
1143 if (mdatrf->mdat_header_size == 16) {
1144 GST_WRITE_UINT32_BE (auxdata, 1);
1145 GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
1146 GST_WRITE_UINT64_BE (auxdata + 8, mdatrf->mdat_size);
1147 } else if (mdatrf->mdat_header_size == 8) {
1148 GST_WRITE_UINT32_BE (auxdata, mdatrf->mdat_size);
1149 GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
1150 } else {
1151 GST_ERROR ("Unexpected atom size: %u", mdatrf->mdat_header_size);
1152 g_assert_not_reached ();
1153 goto fail;
1154 }
1155
1156 if (fwrite (auxdata, 1, mdatrf->mdat_header_size,
1157 outf) != mdatrf->mdat_header_size) {
1158 ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
1159 goto fail;
1160 }
1161
1162 /* now read the mdat data and output to the file */
1163 if (fseek (mdatrf->file, mdatrf->mdat_start +
1164 (mdatrf->rawfile ? 0 : mdatrf->mdat_header_size), SEEK_SET) != 0)
1165 goto fail;
1166
1167 remaining = mdatrf->mdat_size - mdatrf->mdat_header_size;
1168 data = g_malloc (MAX_CHUNK_SIZE);
1169 while (!feof (mdatrf->file) && remaining > 0) {
1170 gint read, write, readsize;
1171
1172 readsize = MIN (MAX_CHUNK_SIZE, remaining);
1173
1174 read = fread (data, 1, readsize, mdatrf->file);
1175 write = fwrite (data, 1, read, outf);
1176 remaining -= read;
1177
1178 if (write != read) {
1179 g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
1180 "Failed to copy data to output file: %s", g_strerror (errno));
1181 goto fail;
1182 }
1183 }
1184 g_free (data);
1185
1186 if (remaining) {
1187 g_set_error (warn, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
1188 "Samples in recovery file were not present on headers."
1189 " Bytes lost: %u", remaining);
1190 } else if (!feof (mdatrf->file)) {
1191 g_set_error (warn, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
1192 "Samples in headers were not found in data file.");
1193 GST_FIXME ("Rewrite mdat size if we reach this to make the file"
1194 " fully correct");
1195 }
1196
1197 return TRUE;
1198
1199 fail:
1200 g_free (stbl_children);
1201 g_free (mvhd_data);
1202 g_free (prefix_data);
1203 g_free (trak_data);
1204 g_free (data);
1205 return FALSE;
1206 }