"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 }