qtmux: Adds moov recovery feature

Adds a new property to qtmux that sets a path to a file to write
and update data about the moov atom (that is not writen till the
end of the file). If the pipeline/app crashes during execution it
might be possible to recover the movie using the qtmoovrecover element.

qtmoovrecover is an element that is also a pipeline. It is not
meant to be used with other elements (it has no pads). It is merely
a tool/utilitary to recover unfinished qtmux files.

Fixes #601576
This commit is contained in:
Thiago Santos 2009-12-12 16:07:15 -03:00 committed by Tim-Philipp Müller
parent 3c0e4d8267
commit b692f9ffb0
10 changed files with 1999 additions and 79 deletions

View file

@ -1,5 +1,5 @@
/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2008 Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br>
* Copyright (C) 2008-2010 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
* Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
*
* This library is free software; you can redistribute it and/or
@ -49,38 +49,6 @@
#include <gst/base/gstbytewriter.h>
/* storage helpers */
#define atom_array_init(array, reserve) \
G_STMT_START { \
(array)->len = 0; \
(array)->size = reserve; \
(array)->data = g_malloc (sizeof (*(array)->data) * reserve); \
} G_STMT_END
#define atom_array_append(array, elmt, inc) \
G_STMT_START { \
g_assert ((array)->data); \
g_assert (inc > 0); \
if (G_UNLIKELY ((array)->len == (array)->size)) { \
(array)->size += inc; \
(array)->data = \
g_realloc ((array)->data, sizeof (*((array)->data)) * (array)->size); \
} \
(array)->data[(array)->len] = elmt; \
(array)->len++; \
} G_STMT_END
#define atom_array_get_len(array) ((array)->len)
#define atom_array_index(array, index) ((array)->data[index])
#define atom_array_clear(array) \
G_STMT_START { \
(array)->size = (array)->len = 0; \
g_free ((array)->data); \
(array)->data = NULL; \
} G_STMT_END
/**
* Creates a new AtomsContext for the given flavor.
*/
@ -641,7 +609,7 @@ atom_stss_clear (AtomSTSS * stss)
atom_array_clear (&stss->entries);
}
static void
void
atom_stbl_init (AtomSTBL * stbl)
{
atom_header_set (&stbl->header, FOURCC_stbl, 0, 0);
@ -656,7 +624,7 @@ atom_stbl_init (AtomSTBL * stbl)
atom_co64_init (&stbl->stco64);
}
static void
void
atom_stbl_clear (AtomSTBL * stbl)
{
atom_clear (&stbl->header);
@ -1370,7 +1338,7 @@ atom_ftyp_copy_data (AtomFTYP * ftyp, guint8 ** buffer, guint64 * size,
return *offset - original_offset;
}
static guint64
guint64
atom_mvhd_copy_data (AtomMVHD * atom, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
@ -1545,7 +1513,7 @@ atom_url_copy_data (AtomURL * url, guint8 ** buffer, guint64 * size,
return original_offset - *offset;
}
static guint64
guint64
atom_stts_copy_data (AtomSTTS * stts, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
@ -1729,7 +1697,7 @@ sample_entry_mp4v_copy_data (SampleTableEntryMP4V * mp4v, guint8 ** buffer,
return *offset - original_offset;
}
static guint64
guint64
atom_stsz_copy_data (AtomSTSZ * stsz, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
@ -1757,7 +1725,7 @@ atom_stsz_copy_data (AtomSTSZ * stsz, guint8 ** buffer, guint64 * size,
return *offset - original_offset;
}
static guint64
guint64
atom_stsc_copy_data (AtomSTSC * stsc, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
@ -1785,7 +1753,7 @@ atom_stsc_copy_data (AtomSTSC * stsc, guint8 ** buffer, guint64 * size,
return *offset - original_offset;
}
static guint64
guint64
atom_ctts_copy_data (AtomCTTS * ctts, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
@ -1811,7 +1779,7 @@ atom_ctts_copy_data (AtomCTTS * ctts, guint8 ** buffer, guint64 * size,
return *offset - original_offset;
}
static guint64
guint64
atom_stco64_copy_data (AtomSTCO64 * stco64, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
@ -1843,7 +1811,7 @@ atom_stco64_copy_data (AtomSTCO64 * stco64, guint8 ** buffer, guint64 * size,
return *offset - original_offset;
}
static guint64
guint64
atom_stss_copy_data (AtomSTSS * stss, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
@ -2152,7 +2120,7 @@ atom_edts_copy_data (AtomEDTS * edts, guint8 ** buffer, guint64 * size,
return *offset - original_offset;
}
static guint64
guint64
atom_trak_copy_data (AtomTRAK * trak, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
@ -2439,12 +2407,10 @@ atom_stbl_add_ctts_entry (AtomSTBL * stbl, guint32 nsamples, guint32 offset)
}
void
atom_trak_add_samples (AtomTRAK * trak, guint32 nsamples, guint32 delta,
atom_stbl_add_samples (AtomSTBL * stbl, guint32 nsamples, guint32 delta,
guint32 size, guint64 chunk_offset, gboolean sync,
gboolean do_pts, gint64 pts_offset)
{
AtomSTBL *stbl = &trak->mdia.minf.stbl;
atom_stts_add_entry (&stbl->stts, nsamples, delta);
atom_stsz_add_entry (&stbl->stsz, nsamples, size);
atom_stco64_add_entry (&stbl->stco64, chunk_offset);
@ -2456,6 +2422,16 @@ atom_trak_add_samples (AtomTRAK * trak, guint32 nsamples, guint32 delta,
atom_stbl_add_ctts_entry (stbl, nsamples, pts_offset);
}
void
atom_trak_add_samples (AtomTRAK * trak, guint32 nsamples, guint32 delta,
guint32 size, guint64 chunk_offset, gboolean sync,
gboolean do_pts, gint64 pts_offset)
{
AtomSTBL *stbl = &trak->mdia.minf.stbl;
atom_stbl_add_samples (stbl, nsamples, delta, size, chunk_offset, sync,
do_pts, pts_offset);
}
/* trak and moov molding */
guint32
@ -2576,7 +2552,7 @@ atom_moov_set_64bits (AtomMOOV * moov, gboolean large_file)
}
}
static void
void
atom_stco64_chunks_add_offset (AtomSTCO64 * stco64, guint32 offset)
{
guint i;

View file

@ -1,5 +1,5 @@
/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2008 Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br>
* Copyright (C) 2008-2010 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -59,6 +59,38 @@ struct { \
struct_type *data; \
}
/* storage helpers */
#define atom_array_init(array, reserve) \
G_STMT_START { \
(array)->len = 0; \
(array)->size = reserve; \
(array)->data = g_malloc (sizeof (*(array)->data) * reserve); \
} G_STMT_END
#define atom_array_append(array, elmt, inc) \
G_STMT_START { \
g_assert ((array)->data); \
g_assert (inc > 0); \
if (G_UNLIKELY ((array)->len == (array)->size)) { \
(array)->size += inc; \
(array)->data = \
g_realloc ((array)->data, sizeof (*((array)->data)) * (array)->size); \
} \
(array)->data[(array)->len] = elmt; \
(array)->len++; \
} G_STMT_END
#define atom_array_get_len(array) ((array)->len)
#define atom_array_index(array, index) ((array)->data[index])
#define atom_array_clear(array) \
G_STMT_START { \
(array)->size = (array)->len = 0; \
g_free ((array)->data); \
(array)->data = NULL; \
} G_STMT_END
/* light-weight context that may influence header atom tree construction */
typedef enum _AtomsTreeFlavor
{
@ -612,6 +644,10 @@ void atom_trak_add_samples (AtomTRAK * trak, guint32 nsamples, guint
void atom_trak_add_elst_entry (AtomTRAK * trak, guint32 duration,
guint32 media_time, guint32 rate);
guint32 atom_trak_get_timescale (AtomTRAK *trak);
void atom_stbl_add_samples (AtomSTBL * stbl, guint32 nsamples,
guint32 delta, guint32 size,
guint64 chunk_offset, gboolean sync,
gboolean do_pts, gint64 pts_offset);
AtomMOOV* atom_moov_new (AtomsContext *context);
void atom_moov_free (AtomMOOV *moov);
@ -622,6 +658,26 @@ void atom_moov_set_64bits (AtomMOOV *moov, gboolean large_file);
void atom_moov_chunks_add_offset (AtomMOOV *moov, guint32 offset);
void atom_moov_add_trak (AtomMOOV *moov, AtomTRAK *trak);
guint64 atom_mvhd_copy_data (AtomMVHD * atom, guint8 ** buffer,
guint64 * size, guint64 * offset);
void atom_stco64_chunks_add_offset (AtomSTCO64 * stco64, guint32 offset);
guint64 atom_trak_copy_data (AtomTRAK * atom, guint8 ** buffer,
guint64 * size, guint64 * offset);
void atom_stbl_clear (AtomSTBL * stbl);
void atom_stbl_init (AtomSTBL * stbl);
guint64 atom_stss_copy_data (AtomSTSS *atom, guint8 **buffer,
guint64 *size, guint64* offset);
guint64 atom_stts_copy_data (AtomSTTS *atom, guint8 **buffer,
guint64 *size, guint64* offset);
guint64 atom_stsc_copy_data (AtomSTSC *atom, guint8 **buffer,
guint64 *size, guint64* offset);
guint64 atom_stsz_copy_data (AtomSTSZ *atom, guint8 **buffer,
guint64 *size, guint64* offset);
guint64 atom_ctts_copy_data (AtomCTTS *atom, guint8 **buffer,
guint64 *size, guint64* offset);
guint64 atom_stco64_copy_data (AtomSTCO64 *atom, guint8 **buffer,
guint64 *size, guint64* offset);
/* media sample description related helpers */
typedef struct

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,159 @@
/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* 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.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __ATOMS_RECOVERY_H__
#define __ATOMS_RECOVERY_H__
#include <glib.h>
#include <string.h>
#include <stdio.h>
#include <gst/gst.h>
#include "atoms.h"
/* Version to be incremented each time we decide
* to change the file layout */
#define ATOMS_RECOV_FILE_VERSION 1
#define ATOMS_RECOV_QUARK (g_quark_from_string ("qtmux-atoms-recovery"))
/* gerror error codes */
#define ATOMS_RECOV_ERR_GENERIC 1
#define ATOMS_RECOV_ERR_FILE 2
#define ATOMS_RECOV_ERR_PARSING 3
#define ATOMS_RECOV_ERR_VERSION 4
/* this struct represents each buffer in a moov file, containing the info
* that is placed in the stsd children atoms
* Fields should be writen in BE order, and booleans should be writen as
* 1byte with 0 for false, anything otherwise */
#define TRAK_BUFFER_ENTRY_INFO_SIZE 34
typedef struct
{
guint32 track_id;
guint32 nsamples;
guint32 delta;
guint32 size;
guint64 chunk_offset;
guint64 pts_offset;
gboolean sync;
gboolean do_pts;
} TrakBufferEntryInfo;
typedef struct
{
guint32 trak_id;
guint32 duration; /* duration in trak timescale */
guint32 timescale; /* trak's timescale */
guint64 file_offset;
/* need for later updating duration */
guint64 tkhd_file_offset;
guint64 mdhd_file_offset;
/* need these offsets to update size */
guint32 trak_size;
guint64 mdia_file_offset;
guint32 mdia_size;
guint64 minf_file_offset;
guint32 minf_size;
guint64 stbl_file_offset;
guint32 stbl_size;
guint64 post_stsd_offset;
guint32 stsd_size;
/* for storing the samples info */
AtomSTBL stbl;
} TrakRecovData;
typedef struct
{
FILE * file;
gboolean rawfile;
/* results from parsing the input file */
guint64 data_size;
guint32 mdat_header_size;
guint mdat_start;
guint64 mdat_size;
} MdatRecovFile;
typedef struct
{
FILE * file;
guint32 timescale;
guint32 mvhd_pos;
guint32 mvhd_size;
guint32 prefix_size; /* prefix + ftyp total size */
gint num_traks;
TrakRecovData *traks_rd;
} MoovRecovFile;
gboolean atoms_recov_write_trak_info (FILE * f, AtomTRAK * trak);
gboolean atoms_recov_write_headers (FILE * f, AtomFTYP * ftyp,
GstBuffer * prefix, AtomMOOV * moov,
guint32 timescale,
guint32 traks_number);
gboolean atoms_recov_write_trak_samples (FILE * f, AtomTRAK * trak,
guint32 nsamples, guint32 delta,
guint32 size, guint64 chunk_offset,
gboolean sync, gboolean do_pts,
gint64 pts_offset);
MdatRecovFile * mdat_recov_file_create (FILE * file, gboolean datafile,
GError ** err);
void mdat_recov_file_free (MdatRecovFile * mrf);
MoovRecovFile * moov_recov_file_create (FILE * file, GError ** err);
void moov_recov_file_free (MoovRecovFile * moovrf);
gboolean moov_recov_parse_buffers (MoovRecovFile * moovrf,
MdatRecovFile * mdatrf,
GError ** err);
gboolean moov_recov_write_file (MoovRecovFile * moovrf,
MdatRecovFile * mdatrf, FILE * outf,
GError ** err);
#endif /* __ATOMS_RECOVERY_H__ */

View file

@ -77,6 +77,7 @@ G_BEGIN_DECLS
#define FOURCC_vmhd GST_MAKE_FOURCC('v','m','h','d')
#define FOURCC_smhd GST_MAKE_FOURCC('s','m','h','d')
#define FOURCC_gmhd GST_MAKE_FOURCC('g','m','h','d')
#define FOURCC_hmhd GST_MAKE_FOURCC('h','m','h','d')
#define FOURCC_gmin GST_MAKE_FOURCC('g','m','i','n')
#define FOURCC_dinf GST_MAKE_FOURCC('d','i','n','f')
#define FOURCC_dref GST_MAKE_FOURCC('d','r','e','f')

View file

@ -0,0 +1,391 @@
/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* 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.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* SECTION:gstqtmoovrecover
* @short_description: Utility element for recovering unfinished quicktime files
*
* <refsect2>
* <para>
* This element recovers quicktime files created with qtmux using the moov recovery feature.
* </para>
* <title>Example pipelines</title>
* <para>
* <programlisting>
* TODO
* </programlisting>
* </refsect2>
*
* Last reviewed on 2010-02-01
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib/gstdio.h>
#include <gst/gst.h>
#include "gstqtmoovrecover.h"
GST_DEBUG_CATEGORY_STATIC (gst_qt_moov_recover_debug);
#define GST_CAT_DEFAULT gst_qt_moov_recover_debug
static GstElementDetails gst_qt_moov_recover_details =
GST_ELEMENT_DETAILS ("QT Moov Recover",
"Util", "Recovers unfinished qtmux files",
"Thiago Santos <thiago.sousa.santos@collabora.co.uk>");
/* QTMoovRecover signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_RECOVERY_INPUT,
PROP_BROKEN_INPUT,
PROP_FIXED_OUTPUT,
PROP_FAST_START_MODE
};
GST_BOILERPLATE (GstQTMoovRecover, gst_qt_moov_recover, GstPipeline,
GST_TYPE_PIPELINE);
/* property functions */
static void gst_qt_moov_recover_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_qt_moov_recover_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static GstStateChangeReturn gst_qt_moov_recover_change_state (GstElement *
element, GstStateChange transition);
static void gst_qt_moov_recover_finalize (GObject * object);
static void
gst_qt_moov_recover_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
#if 0
GstQTMoovRecoverClass *klass = (GstQTMoovRecoverClass *) g_class;
#endif
gst_element_class_set_details (element_class, &gst_qt_moov_recover_details);
}
static void
gst_qt_moov_recover_class_init (GstQTMoovRecoverClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_qt_moov_recover_finalize;
gobject_class->get_property = gst_qt_moov_recover_get_property;
gobject_class->set_property = gst_qt_moov_recover_set_property;
gstelement_class->change_state = gst_qt_moov_recover_change_state;
g_object_class_install_property (gobject_class, PROP_FIXED_OUTPUT,
g_param_spec_string ("fixed-output",
"Path to write the fixed file",
"Path to write the fixed file to (used as output)",
NULL, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_BROKEN_INPUT,
g_param_spec_string ("broken-input",
"Path to broken input file",
"Path to broken input file. (If qtmux was on faststart mode, this "
"file is the faststart file)", NULL, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_RECOVERY_INPUT,
g_param_spec_string ("recovery-input",
"Path to recovery file",
"Path to recovery file (used as input)", NULL, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_FAST_START_MODE,
g_param_spec_boolean ("faststart-mode",
"If the broken input is from faststart mode",
"If the broken input is from faststart mode",
FALSE, G_PARAM_READWRITE));
GST_DEBUG_CATEGORY_INIT (gst_qt_moov_recover_debug, "qtmoovrecover", 0,
"QT Moovie Recover");
}
static void
gst_qt_moov_recover_init (GstQTMoovRecover * qtmr,
GstQTMoovRecoverClass * qtmr_klass)
{
}
static void
gst_qt_moov_recover_finalize (GObject * object)
{
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_qt_moov_recover_run (void *data)
{
FILE *moovrec = NULL;
FILE *mdatinput = NULL;
FILE *output = NULL;
MdatRecovFile *mdat_recov = NULL;
MoovRecovFile *moov_recov = NULL;
GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (data);
GError *err = NULL;
GST_LOG_OBJECT (qtmr, "Starting task");
GST_DEBUG_OBJECT (qtmr, "Validating properties");
GST_OBJECT_LOCK (qtmr);
/* validate properties */
if (qtmr->broken_input == NULL) {
GST_OBJECT_UNLOCK (qtmr);
GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS,
("Please set broken-input property"), NULL);
goto end;
}
if (qtmr->recovery_input == NULL) {
GST_OBJECT_UNLOCK (qtmr);
GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS,
("Please set recovery-input property"), NULL);
goto end;
}
if (qtmr->fixed_output == NULL) {
GST_OBJECT_UNLOCK (qtmr);
GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS,
("Please set fixed-output property"), NULL);
goto end;
}
GST_DEBUG_OBJECT (qtmr, "Opening input/output files");
/* open files */
moovrec = g_fopen (qtmr->recovery_input, "rb");
if (moovrec == NULL) {
GST_OBJECT_UNLOCK (qtmr);
GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ,
("Failed to open recovery-input file"), NULL);
goto end;
}
mdatinput = g_fopen (qtmr->broken_input, "rb");
if (mdatinput == NULL) {
GST_OBJECT_UNLOCK (qtmr);
GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ,
("Failed to open broken-input file"), NULL);
goto end;
}
output = g_fopen (qtmr->fixed_output, "wb+");
if (output == NULL) {
GST_OBJECT_UNLOCK (qtmr);
GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ_WRITE,
("Failed to open fixed-output file"), NULL);
goto end;
}
GST_OBJECT_UNLOCK (qtmr);
GST_DEBUG_OBJECT (qtmr, "Parsing input files");
/* now create our structures */
mdat_recov = mdat_recov_file_create (mdatinput, qtmr->faststart_mode, &err);
mdatinput = NULL;
if (mdat_recov == NULL) {
GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED,
("Broken file could not be parsed correctly"), NULL);
goto end;
}
moov_recov = moov_recov_file_create (moovrec, &err);
moovrec = NULL;
if (moov_recov == NULL) {
GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED,
("Recovery file could not be parsed correctly"), NULL);
goto end;
}
/* now parse the buffers data from moovrec */
if (!moov_recov_parse_buffers (moov_recov, mdat_recov, &err)) {
goto end;
}
GST_DEBUG_OBJECT (qtmr, "Writing fixed file to output");
if (!moov_recov_write_file (moov_recov, mdat_recov, output, &err)) {
goto end;
}
/* here means success */
GST_DEBUG_OBJECT (qtmr, "Finished successfully, posting EOS");
gst_element_post_message (GST_ELEMENT_CAST (qtmr),
gst_message_new_eos (GST_OBJECT_CAST (qtmr)));
end:
GST_LOG_OBJECT (qtmr, "Finalizing task");
if (err) {
GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED, (err->message), NULL);
g_error_free (err);
}
if (moov_recov)
moov_recov_file_free (moov_recov);
if (moovrec)
fclose (moovrec);
if (mdat_recov)
mdat_recov_file_free (mdat_recov);
if (mdatinput)
fclose (mdatinput);
if (output)
fclose (output);
GST_LOG_OBJECT (qtmr, "Leaving task");
gst_task_stop (qtmr->task);
}
static void
gst_qt_moov_recover_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (object);
GST_OBJECT_LOCK (qtmr);
switch (prop_id) {
case PROP_FAST_START_MODE:
g_value_set_boolean (value, qtmr->faststart_mode);
break;
case PROP_BROKEN_INPUT:
g_value_set_string (value, qtmr->broken_input);
break;
case PROP_RECOVERY_INPUT:
g_value_set_string (value, qtmr->recovery_input);
break;
case PROP_FIXED_OUTPUT:
g_value_set_string (value, qtmr->fixed_output);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (qtmr);
}
static void
gst_qt_moov_recover_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (object);
GST_OBJECT_LOCK (qtmr);
switch (prop_id) {
case PROP_FAST_START_MODE:
qtmr->faststart_mode = g_value_get_boolean (value);
break;
case PROP_BROKEN_INPUT:
g_free (qtmr->broken_input);
qtmr->broken_input = g_value_dup_string (value);
break;
case PROP_RECOVERY_INPUT:
g_free (qtmr->recovery_input);
qtmr->recovery_input = g_value_dup_string (value);
break;
case PROP_FIXED_OUTPUT:
g_free (qtmr->fixed_output);
qtmr->fixed_output = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (qtmr);
}
static GstStateChangeReturn
gst_qt_moov_recover_change_state (GstElement * element,
GstStateChange transition)
{
GstStateChangeReturn ret;
GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
qtmr->task = gst_task_create (gst_qt_moov_recover_run, qtmr);
qtmr->task_mutex = g_new (GStaticRecMutex, 1);
g_static_rec_mutex_init (qtmr->task_mutex);
gst_task_set_lock (qtmr->task, qtmr->task_mutex);
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
gst_task_start (qtmr->task);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
gst_task_stop (qtmr->task);
gst_task_join (qtmr->task);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
g_assert (gst_task_get_state (qtmr->task) == GST_TASK_STOPPED);
gst_object_unref (qtmr->task);
qtmr->task = NULL;
g_static_rec_mutex_free (qtmr->task_mutex);
break;
default:
break;
}
return ret;
}
gboolean
gst_qt_moov_recover_register (GstPlugin * plugin)
{
return gst_element_register (plugin, "qtmoovrecover", GST_RANK_NONE,
GST_TYPE_QT_MOOV_RECOVER);
}

View file

@ -0,0 +1,88 @@
/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* 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.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __GST_QT_MOOV_RECOVER_H__
#define __GST_QT_MOOV_RECOVER_H__
#include <gst/gst.h>
#include "atoms.h"
#include "atomsrecovery.h"
G_BEGIN_DECLS
#define GST_TYPE_QT_MOOV_RECOVER (gst_qt_moov_recover_get_type())
#define GST_QT_MOOV_RECOVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QT_MOOV_RECOVER, GstQTMoovRecover))
#define GST_QT_MOOV_RECOVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QT_MOOV_RECOVER, GstQTMoovRecover))
#define GST_IS_QT_MOOV_RECOVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_QT_MOOV_RECOVER))
#define GST_IS_QT_MOOV_RECOVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_QT_MOOV_RECOVER))
#define GST_QT_MOOV_RECOVER_CAST(obj) ((GstQTMoovRecover*)(obj))
typedef struct _GstQTMoovRecover GstQTMoovRecover;
typedef struct _GstQTMoovRecoverClass GstQTMoovRecoverClass;
struct _GstQTMoovRecover
{
GstPipeline pipeline;
GstTask *task;
GStaticRecMutex *task_mutex;
/* properties */
gboolean faststart_mode;
gchar *recovery_input;
gchar *fixed_output;
gchar *broken_input;
};
struct _GstQTMoovRecoverClass
{
GstPipelineClass parent_class;
};
GType gst_qt_moov_recover_get_type (void);
gboolean gst_qt_moov_recover_register (GstPlugin * plugin);
G_END_DECLS
#endif /* __GST_QT_MOOV_RECOVER_H__ */

View file

@ -1,5 +1,5 @@
/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2008 Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br>
* Copyright (C) 2008-2010 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
* Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
*
* This library is free software; you can redistribute it and/or
@ -108,7 +108,8 @@ enum
PROP_DO_CTTS,
PROP_FLAVOR,
PROP_FAST_START,
PROP_FAST_START_TEMP_FILE
PROP_FAST_START_TEMP_FILE,
PROP_MOOV_RECOV_FILE
};
/* some spare for header size as well */
@ -120,6 +121,7 @@ enum
#define DEFAULT_DO_CTTS FALSE
#define DEFAULT_FAST_START FALSE
#define DEFAULT_FAST_START_TEMP_FILE NULL
#define DEFAULT_MOOV_RECOV_FILE NULL
static void gst_qt_mux_finalize (GObject * object);
@ -234,11 +236,19 @@ gst_qt_mux_class_init (GstQTMuxClass * klass)
"when creating a faststart file. If null a filepath will be "
"created automatically", DEFAULT_FAST_START_TEMP_FILE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class, PROP_MOOV_RECOV_FILE,
g_param_spec_string ("moov-recovery-file", "File to store data for "
"posterior moov atom recovery", "File to be used to store "
"data for moov atom making movie file recovery possible in case "
"of a crash during muxing. Null for disabled. (Experimental)",
DEFAULT_MOOV_RECOV_FILE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_qt_mux_request_new_pad);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qt_mux_change_state);
gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_qt_mux_release_pad);
GST_DEBUG_CATEGORY_INIT (gst_qt_mux_debug, "qtmux", 0, "QT Muxer");
}
static void
@ -288,6 +298,10 @@ gst_qt_mux_reset (GstQTMux * qtmux, gboolean alloc)
fclose (qtmux->fast_start_file);
qtmux->fast_start_file = NULL;
}
if (qtmux->moov_recov_file) {
fclose (qtmux->moov_recov_file);
qtmux->moov_recov_file = NULL;
}
gst_tag_setter_reset_tags (GST_TAG_SETTER (qtmux));
/* reset pad data */
@ -350,6 +364,7 @@ gst_qt_mux_finalize (GObject * object)
gst_qt_mux_reset (qtmux, FALSE);
g_free (qtmux->fast_start_file_path);
g_free (qtmux->moov_recov_file_path);
atoms_context_free (qtmux->context);
gst_object_unref (qtmux->collect);
@ -1116,7 +1131,7 @@ gst_qt_mux_send_mdat_header (GstQTMux * qtmux, guint64 * off, guint64 size,
serialize_error:
{
GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL),
("Failed to serialize ftyp"));
("Failed to serialize mdat"));
return GST_FLOW_ERROR;
}
}
@ -1188,26 +1203,48 @@ serialize_error:
}
}
static void
gst_qt_mux_prepare_ftyp (GstQTMux * qtmux, AtomFTYP ** p_ftyp,
GstBuffer ** p_prefix)
{
GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
guint32 major, version;
GList *comp;
GstBuffer *prefix = NULL;
AtomFTYP *ftyp = NULL;
GST_DEBUG_OBJECT (qtmux, "Preparing ftyp and possible prefix atom");
/* init and send context and ftyp based on current property state */
gst_qt_mux_map_format_to_header (qtmux_klass->format, &prefix, &major,
&version, &comp, qtmux->moov, qtmux->longest_chunk,
qtmux->fast_start_file != NULL);
ftyp = atom_ftyp_new (qtmux->context, major, version, comp);
if (comp)
g_list_free (comp);
if (prefix) {
if (p_prefix)
*p_prefix = prefix;
else
gst_buffer_unref (prefix);
}
*p_ftyp = ftyp;
}
static GstFlowReturn
gst_qt_mux_prepare_and_send_ftyp (GstQTMux * qtmux)
{
GstFlowReturn ret = GST_FLOW_OK;
GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
guint32 major, version;
GList *comp;
GstBuffer *prefix;
GstBuffer *prefix = NULL;
GST_DEBUG_OBJECT (qtmux, "Preparing to send ftyp atom");
/* init and send context and ftyp based on current property state */
if (qtmux->ftyp)
if (qtmux->ftyp) {
atom_ftyp_free (qtmux->ftyp);
gst_qt_mux_map_format_to_header (qtmux_klass->format, &prefix, &major,
&version, &comp, qtmux->moov, qtmux->longest_chunk,
qtmux->fast_start_file != NULL);
qtmux->ftyp = atom_ftyp_new (qtmux->context, major, version, comp);
if (comp)
g_list_free (comp);
qtmux->ftyp = NULL;
}
gst_qt_mux_prepare_ftyp (qtmux, &qtmux->ftyp, &prefix);
if (prefix) {
ret = gst_qt_mux_send_buffer (qtmux, prefix, &qtmux->header_size, FALSE);
if (ret != GST_FLOW_OK)
@ -1227,6 +1264,57 @@ gst_qt_mux_start_file (GstQTMux * qtmux)
gst_pad_push_event (qtmux->srcpad,
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0));
/* initialize our moov recovery file */
GST_OBJECT_LOCK (qtmux);
if (qtmux->moov_recov_file_path) {
GST_DEBUG_OBJECT (qtmux, "Openning moov recovery file: %s",
qtmux->moov_recov_file_path);
qtmux->moov_recov_file = g_fopen (qtmux->moov_recov_file_path, "wb+");
if (qtmux->moov_recov_file == NULL) {
GST_WARNING_OBJECT (qtmux, "Failed to open moov recovery file in %s",
qtmux->moov_recov_file_path);
} else {
GSList *walk;
gboolean fail = FALSE;
AtomFTYP *ftyp = NULL;
GstBuffer *prefix = NULL;
gst_qt_mux_prepare_ftyp (qtmux, &ftyp, &prefix);
if (!atoms_recov_write_headers (qtmux->moov_recov_file, ftyp, prefix,
qtmux->moov, qtmux->timescale,
g_slist_length (qtmux->collect->data))) {
GST_WARNING_OBJECT (qtmux, "Failed to write moov recovery file "
"headers");
fail = TRUE;
}
atom_ftyp_free (ftyp);
if (prefix)
gst_buffer_unref (prefix);
for (walk = qtmux->collect->data; walk && !fail;
walk = g_slist_next (walk)) {
GstCollectData *cdata = (GstCollectData *) walk->data;
GstQTPad *qpad = (GstQTPad *) cdata;
/* write info for each stream */
fail = atoms_recov_write_trak_info (qtmux->moov_recov_file, qpad->trak);
if (fail) {
GST_WARNING_OBJECT (qtmux, "Failed to write trak info to recovery "
"file");
}
}
if (fail) {
/* cleanup */
fclose (qtmux->moov_recov_file);
qtmux->moov_recov_file = NULL;
GST_WARNING_OBJECT (qtmux, "An error was detected while writing to "
"recover file, moov recovery won't work");
}
}
}
GST_OBJECT_UNLOCK (qtmux);
/*
* send mdat header if already needed, and mark position for later update.
* We don't send ftyp now if we are on fast start mode, because we can
@ -1606,6 +1694,16 @@ gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
/* now we go and register this buffer/sample all over */
/* note that a new chunk is started each time (not fancy but works) */
if (qtmux->moov_recov_file) {
if (!atoms_recov_write_trak_samples (qtmux->moov_recov_file, pad->trak,
nsamples, scaled_duration, sample_size, chunk_offset, sync, do_pts,
pts_offset)) {
GST_WARNING_OBJECT (qtmux, "Failed to write sample information to "
"recovery file, disabling recovery");
fclose (qtmux->moov_recov_file);
qtmux->moov_recov_file = NULL;
}
}
atom_trak_add_samples (pad->trak, nsamples, scaled_duration, sample_size,
chunk_offset, sync, do_pts, pts_offset);
@ -2451,6 +2549,9 @@ gst_qt_mux_get_property (GObject * object,
case PROP_FAST_START_TEMP_FILE:
g_value_set_string (value, qtmux->fast_start_file_path);
break;
case PROP_MOOV_RECOV_FILE:
g_value_set_string (value, qtmux->moov_recov_file_path);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -2499,6 +2600,10 @@ gst_qt_mux_set_property (GObject * object,
gst_qt_mux_generate_fast_start_file_path (qtmux);
}
break;
case PROP_MOOV_RECOV_FILE:
g_free (qtmux->moov_recov_file_path);
qtmux->moov_recov_file_path = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -2545,7 +2650,6 @@ gst_qt_mux_change_state (GstElement * element, GstStateChange transition)
return ret;
}
gboolean
gst_qt_mux_register (GstPlugin * plugin)
{
@ -2613,18 +2717,3 @@ gst_qt_mux_register (GstPlugin * plugin)
return TRUE;
}
gboolean
gst_qt_mux_plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_qt_mux_debug, "qtmux", 0, "QT Muxer");
return gst_qt_mux_register (plugin);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"qtmux",
"Quicktime Muxer plugin",
gst_qt_mux_plugin_init, VERSION, "LGPL", "gsoc2008 package",
"embedded.ufcg.edu.br")

View file

@ -1,5 +1,5 @@
/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2008 Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br>
* Copyright (C) 2008-2010 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -48,6 +48,7 @@
#include "fourcc.h"
#include "atoms.h"
#include "atomsrecovery.h"
#include "gstqtmuxmap.h"
G_BEGIN_DECLS
@ -144,6 +145,9 @@ struct _GstQTMux
/* fast start */
FILE *fast_start_file;
/* moov recovery */
FILE *moov_recov_file;
/* properties */
guint32 timescale;
AtomsTreeFlavor flavor;
@ -151,6 +155,7 @@ struct _GstQTMux
gboolean large_file;
gboolean guess_pts;
gchar *fast_start_file_path;
gchar *moov_recov_file_path;
/* for collect pads event handling function */
GstPadEventFunction collect_event;
@ -178,6 +183,7 @@ typedef struct _GstQTMuxClassParams
#define GST_QT_MUX_PARAMS_QDATA g_quark_from_static_string("qt-mux-params")
GType gst_qt_mux_get_type (void);
gboolean gst_qt_mux_register (GstPlugin * plugin);
/* FIXME: ideally classification tag should be added and
* registered in gstreamer core gsttaglist

View file

@ -0,0 +1,67 @@
/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2008-2010 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
* Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* 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.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstqtmux.h"
#include "gstqtmoovrecover.h"
static gboolean
gst_qt_mux_plugin_init (GstPlugin * plugin)
{
if (!gst_qt_mux_register (plugin))
return FALSE;
if (!gst_qt_moov_recover_register (plugin))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"qtmux",
"Quicktime Muxer plugin",
gst_qt_mux_plugin_init, VERSION, "LGPL", "gsoc2008 package",
"embedded.ufcg.edu.br")