Use new gst_element_class_set_static_metadata()
[platform/upstream/gstreamer.git] / gst / isomp4 / gstqtmoovrecover.c
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, 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 /**
45  * SECTION:element-qtmoovrecover
46  * @short_description: Utility element for recovering unfinished quicktime files
47  *
48  * <refsect2>
49  * <para>
50  * This element recovers quicktime files created with qtmux using the moov
51  * recovery feature.
52  * </para>
53  * <title>Example pipelines</title>
54  * <para>
55  * <programlisting>
56  * TODO
57  * </programlisting>
58  * </para>
59  * </refsect2>
60  *
61  * Documentation last reviewed on 2011-04-21
62  */
63
64 #ifdef HAVE_CONFIG_H
65 #include "config.h"
66 #endif
67
68 #include <glib/gstdio.h>
69 #include <gst/gst.h>
70
71 #include "gstqtmoovrecover.h"
72
73 GST_DEBUG_CATEGORY_STATIC (gst_qt_moov_recover_debug);
74 #define GST_CAT_DEFAULT gst_qt_moov_recover_debug
75
76 /* QTMoovRecover signals and args */
77 enum
78 {
79   /* FILL ME */
80   LAST_SIGNAL
81 };
82
83 enum
84 {
85   PROP_0,
86   PROP_RECOVERY_INPUT,
87   PROP_BROKEN_INPUT,
88   PROP_FIXED_OUTPUT,
89   PROP_FAST_START_MODE
90 };
91
92 #define gst_qt_moov_recover_parent_class parent_class
93 G_DEFINE_TYPE (GstQTMoovRecover, gst_qt_moov_recover, GST_TYPE_PIPELINE);
94
95 /* property functions */
96 static void gst_qt_moov_recover_set_property (GObject * object,
97     guint prop_id, const GValue * value, GParamSpec * pspec);
98 static void gst_qt_moov_recover_get_property (GObject * object,
99     guint prop_id, GValue * value, GParamSpec * pspec);
100
101 static GstStateChangeReturn gst_qt_moov_recover_change_state (GstElement *
102     element, GstStateChange transition);
103
104 static void gst_qt_moov_recover_finalize (GObject * object);
105
106 static void
107 gst_qt_moov_recover_class_init (GstQTMoovRecoverClass * klass)
108 {
109   GObjectClass *gobject_class;
110   GstElementClass *gstelement_class;
111
112   gobject_class = (GObjectClass *) klass;
113   gstelement_class = (GstElementClass *) klass;
114
115   parent_class = g_type_class_peek_parent (klass);
116
117   gobject_class->finalize = gst_qt_moov_recover_finalize;
118   gobject_class->get_property = gst_qt_moov_recover_get_property;
119   gobject_class->set_property = gst_qt_moov_recover_set_property;
120
121   gstelement_class->change_state = gst_qt_moov_recover_change_state;
122
123   g_object_class_install_property (gobject_class, PROP_FIXED_OUTPUT,
124       g_param_spec_string ("fixed-output",
125           "Path to write the fixed file",
126           "Path to write the fixed file to (used as output)",
127           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
128   g_object_class_install_property (gobject_class, PROP_BROKEN_INPUT,
129       g_param_spec_string ("broken-input",
130           "Path to broken input file",
131           "Path to broken input file. (If qtmux was on faststart mode, this "
132           "file is the faststart file)", NULL,
133           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
134   g_object_class_install_property (gobject_class, PROP_RECOVERY_INPUT,
135       g_param_spec_string ("recovery-input",
136           "Path to recovery file",
137           "Path to recovery file (used as input)", NULL,
138           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
139   g_object_class_install_property (gobject_class, PROP_FAST_START_MODE,
140       g_param_spec_boolean ("faststart-mode",
141           "If the broken input is from faststart mode",
142           "If the broken input is from faststart mode",
143           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
144
145   GST_DEBUG_CATEGORY_INIT (gst_qt_moov_recover_debug, "qtmoovrecover", 0,
146       "QT Moovie Recover");
147
148   gst_element_class_set_static_metadata (gstelement_class, "QT Moov Recover",
149       "Util", "Recovers unfinished qtmux files",
150       "Thiago Santos <thiago.sousa.santos@collabora.co.uk>");
151 }
152
153 static void
154 gst_qt_moov_recover_init (GstQTMoovRecover * qtmr)
155 {
156 }
157
158 static void
159 gst_qt_moov_recover_finalize (GObject * object)
160 {
161   G_OBJECT_CLASS (parent_class)->finalize (object);
162 }
163
164 static void
165 gst_qt_moov_recover_run (void *data)
166 {
167   FILE *moovrec = NULL;
168   FILE *mdatinput = NULL;
169   FILE *output = NULL;
170   MdatRecovFile *mdat_recov = NULL;
171   MoovRecovFile *moov_recov = NULL;
172   GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (data);
173   GError *err = NULL;
174
175   GST_LOG_OBJECT (qtmr, "Starting task");
176
177   GST_DEBUG_OBJECT (qtmr, "Validating properties");
178   GST_OBJECT_LOCK (qtmr);
179   /* validate properties */
180   if (qtmr->broken_input == NULL) {
181     GST_OBJECT_UNLOCK (qtmr);
182     GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS,
183         ("Please set broken-input property"), (NULL));
184     goto end;
185   }
186   if (qtmr->recovery_input == NULL) {
187     GST_OBJECT_UNLOCK (qtmr);
188     GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS,
189         ("Please set recovery-input property"), (NULL));
190     goto end;
191   }
192   if (qtmr->fixed_output == NULL) {
193     GST_OBJECT_UNLOCK (qtmr);
194     GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS,
195         ("Please set fixed-output property"), (NULL));
196     goto end;
197   }
198
199   GST_DEBUG_OBJECT (qtmr, "Opening input/output files");
200   /* open files */
201   moovrec = g_fopen (qtmr->recovery_input, "rb");
202   if (moovrec == NULL) {
203     GST_OBJECT_UNLOCK (qtmr);
204     GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ,
205         ("Failed to open recovery-input file"), (NULL));
206     goto end;
207   }
208
209   mdatinput = g_fopen (qtmr->broken_input, "rb");
210   if (mdatinput == NULL) {
211     GST_OBJECT_UNLOCK (qtmr);
212     GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ,
213         ("Failed to open broken-input file"), (NULL));
214     goto end;
215   }
216   output = g_fopen (qtmr->fixed_output, "wb+");
217   if (output == NULL) {
218     GST_OBJECT_UNLOCK (qtmr);
219     GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ_WRITE,
220         ("Failed to open fixed-output file"), (NULL));
221     goto end;
222   }
223   GST_OBJECT_UNLOCK (qtmr);
224
225   GST_DEBUG_OBJECT (qtmr, "Parsing input files");
226   /* now create our structures */
227   mdat_recov = mdat_recov_file_create (mdatinput, qtmr->faststart_mode, &err);
228   mdatinput = NULL;
229   if (mdat_recov == NULL) {
230     GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED,
231         ("Broken file could not be parsed correctly"), (NULL));
232     goto end;
233   }
234   moov_recov = moov_recov_file_create (moovrec, &err);
235   moovrec = NULL;
236   if (moov_recov == NULL) {
237     GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED,
238         ("Recovery file could not be parsed correctly"), (NULL));
239     goto end;
240   }
241
242   /* now parse the buffers data from moovrec */
243   if (!moov_recov_parse_buffers (moov_recov, mdat_recov, &err)) {
244     goto end;
245   }
246
247   GST_DEBUG_OBJECT (qtmr, "Writing fixed file to output");
248   if (!moov_recov_write_file (moov_recov, mdat_recov, output, &err)) {
249     goto end;
250   }
251
252   /* here means success */
253   GST_DEBUG_OBJECT (qtmr, "Finished successfully, posting EOS");
254   gst_element_post_message (GST_ELEMENT_CAST (qtmr),
255       gst_message_new_eos (GST_OBJECT_CAST (qtmr)));
256
257 end:
258   GST_LOG_OBJECT (qtmr, "Finalizing task");
259   if (err) {
260     GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED, ("%s", err->message), (NULL));
261     g_error_free (err);
262   }
263
264   if (moov_recov)
265     moov_recov_file_free (moov_recov);
266   if (moovrec)
267     fclose (moovrec);
268
269   if (mdat_recov)
270     mdat_recov_file_free (mdat_recov);
271   if (mdatinput)
272     fclose (mdatinput);
273
274   if (output)
275     fclose (output);
276   GST_LOG_OBJECT (qtmr, "Leaving task");
277   gst_task_stop (qtmr->task);
278 }
279
280 static void
281 gst_qt_moov_recover_get_property (GObject * object,
282     guint prop_id, GValue * value, GParamSpec * pspec)
283 {
284   GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (object);
285
286   GST_OBJECT_LOCK (qtmr);
287   switch (prop_id) {
288     case PROP_FAST_START_MODE:
289       g_value_set_boolean (value, qtmr->faststart_mode);
290       break;
291     case PROP_BROKEN_INPUT:
292       g_value_set_string (value, qtmr->broken_input);
293       break;
294     case PROP_RECOVERY_INPUT:
295       g_value_set_string (value, qtmr->recovery_input);
296       break;
297     case PROP_FIXED_OUTPUT:
298       g_value_set_string (value, qtmr->fixed_output);
299       break;
300     default:
301       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
302       break;
303   }
304   GST_OBJECT_UNLOCK (qtmr);
305 }
306
307 static void
308 gst_qt_moov_recover_set_property (GObject * object,
309     guint prop_id, const GValue * value, GParamSpec * pspec)
310 {
311   GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (object);
312
313   GST_OBJECT_LOCK (qtmr);
314   switch (prop_id) {
315     case PROP_FAST_START_MODE:
316       qtmr->faststart_mode = g_value_get_boolean (value);
317       break;
318     case PROP_BROKEN_INPUT:
319       g_free (qtmr->broken_input);
320       qtmr->broken_input = g_value_dup_string (value);
321       break;
322     case PROP_RECOVERY_INPUT:
323       g_free (qtmr->recovery_input);
324       qtmr->recovery_input = g_value_dup_string (value);
325       break;
326     case PROP_FIXED_OUTPUT:
327       g_free (qtmr->fixed_output);
328       qtmr->fixed_output = g_value_dup_string (value);
329       break;
330     default:
331       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332       break;
333   }
334   GST_OBJECT_UNLOCK (qtmr);
335 }
336
337 static GstStateChangeReturn
338 gst_qt_moov_recover_change_state (GstElement * element,
339     GstStateChange transition)
340 {
341   GstStateChangeReturn ret;
342   GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (element);
343
344   switch (transition) {
345     case GST_STATE_CHANGE_NULL_TO_READY:
346       qtmr->task = gst_task_new (gst_qt_moov_recover_run, qtmr);
347       g_rec_mutex_init (&qtmr->task_mutex);
348       gst_task_set_lock (qtmr->task, &qtmr->task_mutex);
349       break;
350     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
351       gst_task_start (qtmr->task);
352       break;
353     default:
354       break;
355   }
356
357   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
358
359   switch (transition) {
360     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
361       gst_task_stop (qtmr->task);
362       gst_task_join (qtmr->task);
363       break;
364     case GST_STATE_CHANGE_READY_TO_NULL:
365       g_assert (gst_task_get_state (qtmr->task) == GST_TASK_STOPPED);
366       gst_object_unref (qtmr->task);
367       qtmr->task = NULL;
368       g_rec_mutex_clear (&qtmr->task_mutex);
369       break;
370     default:
371       break;
372   }
373   return ret;
374 }
375
376
377 gboolean
378 gst_qt_moov_recover_register (GstPlugin * plugin)
379 {
380   return gst_element_register (plugin, "qtmoovrecover", GST_RANK_NONE,
381       GST_TYPE_QT_MOOV_RECOVER);
382 }