*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* SOFTWARE.
*/
-/**
+/*
* This module contains functions for serializing partial information from
* a mux in progress (by qtmux elements). This enables reconstruction of the
* moov box if a crash happens and thus recovering the movie file.
*
* 2) CRASH!
*
- * 3) gst-launch qtmoovrecover recovery-input=path.mrf broken-input=moovie.mov \
+ * 3) gst-launch-1.0 qtmoovrecover recovery-input=path.mrf broken-input=moovie.mov \
fixed-output=recovered.mov
*
* 4) (Hopefully) enjoy recovered.mov.
#include "atomsrecovery.h"
+#define MAX_CHUNK_SIZE (1024 * 1024) /* 1MB */
+
#define ATOMS_RECOV_OUTPUT_WRITE_ERROR(err) \
g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, \
"Failed to write to output file: %s", g_strerror (errno))
guint64 size = 0;
if (prefix) {
- guint8 *bdata;
- gsize bsize;
+ GstMapInfo map;
- bdata = gst_buffer_map (prefix, &bsize, NULL, GST_MAP_READ);
- if (fwrite (bdata, 1, bsize, f) != bsize) {
- gst_buffer_unmap (prefix, bdata, bsize);
+ if (!gst_buffer_map (prefix, &map, GST_MAP_READ)) {
+ return FALSE;
+ }
+ if (fwrite (map.data, 1, map.size, f) != map.size) {
+ gst_buffer_unmap (prefix, &map);
return FALSE;
}
- gst_buffer_unmap (prefix, bdata, bsize);
+ gst_buffer_unmap (prefix, &map);
}
if (!atom_ftyp_copy_data (ftyp, &data, &size, &offset)) {
return FALSE;
}
if (fwrite (data, 1, offset, f) != offset) {
+ g_free (data);
return FALSE;
}
+ g_free (data);
return TRUE;
}
-/**
+/*
* Writes important info on the 'moov' atom (non-trak related)
* to be able to recover the moov structure after a crash.
*
return atom_size > 0 && writen == atom_size;
}
-/**
+/*
* Writes the number of traks to the file.
* This simply writes a guint32 in BE.
*/
return fwrite (data, 4, 1, f) == 1;
}
-/**
+/*
* Writes the moov's timescale to the file
* This simply writes a guint32 in BE.
*/
return fwrite (data, 4, 1, f) == 1;
}
-/**
+/*
* Writes the trak atom to the file.
*/
gboolean
return fourcc == FOURCC_mdat;
}
+static gboolean
+mdat_recov_file_find_mdat (FILE * file, GError ** err)
+{
+ guint32 fourcc = 0, size = 0;
+ gboolean failure = FALSE;
+ while (fourcc != FOURCC_mdat && !failure) {
+ if (!read_atom_header (file, &fourcc, &size)) {
+ goto parse_error;
+ }
+ switch (fourcc) {
+ /* skip these atoms */
+ case FOURCC_ftyp:
+ case FOURCC_free:
+ case FOURCC_udta:
+ if (fseek (file, size - 8, SEEK_CUR) != 0) {
+ goto file_seek_error;
+ }
+ break;
+ case FOURCC_mdat:
+ break;
+ default:
+ GST_ERROR ("Unexpected atom in headers %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (fourcc));
+ failure = TRUE;
+ break;
+ }
+ }
+
+ if (!failure) {
+ /* Reverse to mdat start */
+ if (fseek (file, -8, SEEK_CUR) != 0)
+ goto file_seek_error;
+ }
+
+ return !failure;
+
+parse_error:
+ g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
+ "Failed to parse atom");
+ return FALSE;
+
+file_seek_error:
+ g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
+ "Failed to seek to start of the file");
+ return FALSE;
+
+}
+
MdatRecovFile *
mdat_recov_file_create (FILE * file, gboolean datafile, GError ** err)
{
MdatRecovFile *mrf = g_new0 (MdatRecovFile, 1);
- guint32 fourcc, size;
g_return_val_if_fail (file != NULL, NULL);
return mrf;
}
- if (!read_atom_header (file, &fourcc, &size)) {
- goto parse_error;
- }
- if (fourcc != FOURCC_ftyp) {
- /* this could be a prefix atom, let's skip it and try again */
- if (fseek (file, size - 8, SEEK_CUR) != 0) {
- goto file_seek_error;
- }
- if (!read_atom_header (file, &fourcc, &size)) {
- goto parse_error;
- }
- }
-
- if (fourcc != FOURCC_ftyp) {
- goto parse_error;
+ if (!mdat_recov_file_find_mdat (file, err)) {
+ goto fail;
}
- if (fseek (file, size - 8, SEEK_CUR) != 0)
- goto file_seek_error;
/* we don't parse this if we have a tmpdatafile */
if (!mdat_recov_file_parse_mdat_start (mrf)) {
return mrf;
-parse_error:
- g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
- "Failed to parse atom");
- goto fail;
-
file_seek_error:
g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
"Failed to seek to start of the file");
return FALSE;
/* advance the rest of tkhd */
- fseek (moovrf->file, 68, SEEK_CUR);
+ if (fseek (moovrf->file, 68, SEEK_CUR) != 0)
+ return FALSE;
trakrd->trak_id = GST_READ_UINT32_BE (data);
return TRUE;
if (!moov_recov_parse_mdia (moovrf, trakrd))
return FALSE;
+ if (fseek (moovrf->file,
+ (long int) trakrd->mdia_file_offset + trakrd->mdia_size,
+ SEEK_SET) != 0)
+ return FALSE;
+
+ trakrd->extra_atoms_offset = ftell (moovrf->file);
+ trakrd->extra_atoms_size = size - (trakrd->extra_atoms_offset - offset);
+
trakrd->file_offset = offset;
/* position after the trak */
return fseek (moovrf->file, (long int) offset + size, SEEK_SET) == 0;
goto fail;
}
+ /* sanity check */
+ if (moovrf->num_traks > 1024) {
+ g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
+ "Unsupported number of traks");
+ goto fail;
+ }
+
/* init the traks */
moovrf->traks_rd = g_new0 (TrakRecovData, moovrf->num_traks);
for (i = 0; i < moovrf->num_traks; i++) {
b->chunk_offset, b->sync, b->pts_offset);
}
-/**
+/*
* Parses the buffer entries in the MoovRecovFile and matches the inputs
* with the data in the MdatRecovFile. Whenever a buffer entry of that
* represents 'x' bytes of data, the same amount of data is 'validated' in
return NULL;
}
+static gboolean
+copy_data_from_file_to_file (FILE * from, guint position, guint size, FILE * to,
+ GError ** err)
+{
+ guint8 *data = NULL;
+
+ if (fseek (from, position, SEEK_SET) != 0)
+ goto fail;
+ data = g_malloc (size);
+ if (fread (data, 1, size, from) != size) {
+ goto fail;
+ }
+ if (fwrite (data, 1, size, to) != size) {
+ ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
+ goto fail;
+ }
+
+ g_free (data);
+ return TRUE;
+
+fail:
+ g_free (data);
+ return FALSE;
+}
+
gboolean
moov_recov_write_file (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
- FILE * outf, GError ** err)
+ FILE * outf, GError ** err, GError ** warn)
{
guint8 auxdata[16];
guint8 *data = NULL;
guint8 *stbl_children = NULL;
guint32 longest_duration = 0;
guint16 version;
+ guint remaining;
/* check the version */
if (fseek (moovrf->file, 0, SEEK_SET) != 0) {
/* add chunks offsets */
for (i = 0; i < moovrf->num_traks; i++) {
TrakRecovData *trak = &(moovrf->traks_rd[i]);
- /* 16 for the mdat header */
- gint64 offset = moov_size + ftell (outf) + 16;
- atom_stco64_chunks_add_offset (&trak->stbl.stco64, offset);
+ /* 8 or 16 for the mdat header */
+ gint64 offset = moov_size + ftell (outf) + mdatrf->mdat_header_size;
+ atom_stco64_chunks_set_offset (&trak->stbl.stco64, offset);
}
/* write the moov */
ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
goto fail;
}
+
g_free (trak_data);
trak_data = NULL;
g_free (stbl_children);
stbl_children = NULL;
+
+ /* Copy the extra atoms after 'minf' */
+ if (!copy_data_from_file_to_file (moovrf->file, trak->extra_atoms_offset,
+ trak->extra_atoms_size, outf, err))
+ goto fail;
}
/* write the mdat */
/* write the header first */
- GST_WRITE_UINT32_BE (auxdata, 1);
- GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
- GST_WRITE_UINT64_BE (auxdata + 8, mdatrf->mdat_size);
- if (fwrite (auxdata, 1, 16, outf) != 16) {
+ if (mdatrf->mdat_header_size == 16) {
+ GST_WRITE_UINT32_BE (auxdata, 1);
+ GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
+ GST_WRITE_UINT64_BE (auxdata + 8, mdatrf->mdat_size);
+ } else if (mdatrf->mdat_header_size == 8) {
+ GST_WRITE_UINT32_BE (auxdata, mdatrf->mdat_size);
+ GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
+ } else {
+ GST_ERROR ("Unexpected atom size: %u", mdatrf->mdat_header_size);
+ g_assert_not_reached ();
+ goto fail;
+ }
+
+ if (fwrite (auxdata, 1, mdatrf->mdat_header_size,
+ outf) != mdatrf->mdat_header_size) {
ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
goto fail;
}
(mdatrf->rawfile ? 0 : mdatrf->mdat_header_size), SEEK_SET) != 0)
goto fail;
- data = g_malloc (4096);
- while (!feof (mdatrf->file)) {
- gint read, write;
+ remaining = mdatrf->mdat_size - mdatrf->mdat_header_size;
+ data = g_malloc (MAX_CHUNK_SIZE);
+ while (!feof (mdatrf->file) && remaining > 0) {
+ gint read, write, readsize;
+
+ readsize = MIN (MAX_CHUNK_SIZE, remaining);
- read = fread (data, 1, 4096, mdatrf->file);
+ read = fread (data, 1, readsize, mdatrf->file);
write = fwrite (data, 1, read, outf);
+ remaining -= read;
if (write != read) {
g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
}
g_free (data);
+ if (remaining) {
+ g_set_error (warn, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
+ "Samples in recovery file were not present on headers."
+ " Bytes lost: %u", remaining);
+ } else if (!feof (mdatrf->file)) {
+ g_set_error (warn, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
+ "Samples in headers were not found in data file.");
+ GST_FIXME ("Rewrite mdat size if we reach this to make the file"
+ " fully correct");
+ }
+
return TRUE;
fail: