various: fix pad template leaks
[platform/upstream/gstreamer.git] / gst / multifile / gstmultifilesink.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2006 Wim Taymans <wim@fluendo.com>
5  *                    2006 David A. Schleef <ds@schleef.org>
6  *
7  * gstmultifilesink.c:
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24 /**
25  * SECTION:element-multifilesink
26  * @see_also: #GstFileSrc
27  *
28  * Write incoming data to a series of sequentially-named files.
29  *
30  * The filename property should contain a string with a \%d placeholder that will
31  * be substituted with the index for each filename.
32  *
33  * If the #GstMultiFileSink:post-messages property is #TRUE, it sends an application
34  * message named
35  * <classname>&quot;GstMultiFileSink&quot;</classname> after writing each
36  * buffer.
37  *
38  * The message's structure contains these fields:
39  * <itemizedlist>
40  * <listitem>
41  *   <para>
42  *   #gchar *
43  *   <classname>&quot;filename&quot;</classname>:
44  *   the filename where the buffer was written.
45  *   </para>
46  * </listitem>
47  * <listitem>
48  *   <para>
49  *   #gint
50  *   <classname>&quot;index&quot;</classname>:
51  *   the index of the buffer.
52  *   </para>
53  * </listitem>
54  * <listitem>
55  *   <para>
56  *   #GstClockTime
57  *   <classname>&quot;timestamp&quot;</classname>:
58  *   the timestamp of the buffer.
59  *   </para>
60  * </listitem>
61  * <listitem>
62  *   <para>
63  *   #GstClockTime
64  *   <classname>&quot;stream-time&quot;</classname>:
65  *   the stream time of the buffer.
66  *   </para>
67  * </listitem>
68  * <listitem>
69  *   <para>
70  *   #GstClockTime
71  *   <classname>&quot;running-time&quot;</classname>:
72  *   the running_time of the buffer.
73  *   </para>
74  * </listitem>
75  * <listitem>
76  *   <para>
77  *   #GstClockTime
78  *   <classname>&quot;duration&quot;</classname>:
79  *   the duration of the buffer.
80  *   </para>
81  * </listitem>
82  * <listitem>
83  *   <para>
84  *   #guint64
85  *   <classname>&quot;offset&quot;</classname>:
86  *   the offset of the buffer that triggered the message.
87  *   </para>
88  * </listitem>
89  * <listitem>
90  *   <para>
91  *   #guint64
92  *   <classname>&quot;offset-end&quot;</classname>:
93  *   the offset-end of the buffer that triggered the message.
94  *   </para>
95  * </listitem>
96  * </itemizedlist>
97  *
98  * <refsect2>
99  * <title>Example launch line</title>
100  * |[
101  * gst-launch audiotestsrc ! multifilesink
102  * gst-launch videotestsrc ! multifilesink post-messages=true filename="frame%d"
103  * ]|
104  * </refsect2>
105  *
106  * Last reviewed on 2009-09-11 (0.10.17)
107  */
108
109 #ifdef HAVE_CONFIG_H
110 #  include "config.h"
111 #endif
112 #include <gst/base/gstbasetransform.h>
113 #include <glib/gstdio.h>
114 #include "gstmultifilesink.h"
115
116 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
117     GST_PAD_SINK,
118     GST_PAD_ALWAYS,
119     GST_STATIC_CAPS_ANY);
120
121 GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug);
122 #define GST_CAT_DEFAULT gst_multi_file_sink_debug
123
124 #define DEFAULT_LOCATION "%05d"
125 #define DEFAULT_INDEX 0
126 #define DEFAULT_POST_MESSAGES FALSE
127 #define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER
128 #define DEFAULT_MAX_FILES 0
129
130 enum
131 {
132   PROP_0,
133   PROP_LOCATION,
134   PROP_INDEX,
135   PROP_POST_MESSAGES,
136   PROP_NEXT_FILE,
137   PROP_MAX_FILES,
138   PROP_LAST
139 };
140
141 static void gst_multi_file_sink_finalize (GObject * object);
142
143 static void gst_multi_file_sink_set_property (GObject * object, guint prop_id,
144     const GValue * value, GParamSpec * pspec);
145 static void gst_multi_file_sink_get_property (GObject * object, guint prop_id,
146     GValue * value, GParamSpec * pspec);
147
148 static gboolean gst_multi_file_sink_stop (GstBaseSink * sink);
149 static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
150     GstBuffer * buffer);
151 static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink,
152     GstCaps * caps);
153 static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink *
154     multifilesink);
155 static void gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
156     GstBuffer * buffer);
157 static void gst_multi_file_sink_ensure_max_files (GstMultiFileSink *
158     multifilesink);
159
160 #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
161 static GType
162 gst_multi_file_sink_next_get_type (void)
163 {
164   static GType multi_file_sync_next_type = 0;
165   static const GEnumValue next_types[] = {
166     {GST_MULTI_FILE_SINK_NEXT_BUFFER, "New file for each buffer", "buffer"},
167     {GST_MULTI_FILE_SINK_NEXT_DISCONT, "New file after each discontinuity",
168         "discont"},
169     {GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, "New file at each key frame "
170           "(Useful for MPEG-TS segmenting)", "key-frame"},
171     {0, NULL, NULL}
172   };
173
174   if (!multi_file_sync_next_type) {
175     multi_file_sync_next_type =
176         g_enum_register_static ("GstMultiFileSinkNext", next_types);
177   }
178
179   return multi_file_sync_next_type;
180 }
181
182 GST_BOILERPLATE (GstMultiFileSink, gst_multi_file_sink, GstBaseSink,
183     GST_TYPE_BASE_SINK);
184
185 static void
186 gst_multi_file_sink_base_init (gpointer g_class)
187 {
188   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
189
190   GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
191       "multifilesink element");
192
193   gst_element_class_add_static_pad_template (gstelement_class,
194       &sinktemplate);
195   gst_element_class_set_details_simple (gstelement_class, "Multi-File Sink",
196       "Sink/File",
197       "Write buffers to a sequentially named set of files",
198       "David Schleef <ds@schleef.org>");
199 }
200
201 static void
202 gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
203 {
204   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
205   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
206
207   gobject_class->set_property = gst_multi_file_sink_set_property;
208   gobject_class->get_property = gst_multi_file_sink_get_property;
209
210   g_object_class_install_property (gobject_class, PROP_LOCATION,
211       g_param_spec_string ("location", "File Location",
212           "Location of the file to write", NULL,
213           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
214
215   g_object_class_install_property (gobject_class, PROP_INDEX,
216       g_param_spec_int ("index", "Index",
217           "Index to use with location property to create file names.  The "
218           "index is incremented by one for each buffer written.",
219           0, G_MAXINT, DEFAULT_INDEX,
220           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221   /**
222    * GstMultiFileSink:post-messages
223    *
224    * Post a message on the GstBus for each file.
225    *
226    * Since: 0.10.17
227    */
228   g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
229       g_param_spec_boolean ("post-messages", "Post Messages",
230           "Post a message for each file with information of the buffer",
231           DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
232   /**
233    * GstMultiFileSink:next-file
234    *
235    * When to start a new file.
236    *
237    * Since: 0.10.17
238    */
239   g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
240       g_param_spec_enum ("next-file", "Next File",
241           "When to start a new file",
242           GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
243           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
244
245
246   /**
247    * GstMultiFileSink:max-files
248    *
249    * Maximum number of files to keep on disk. Once the maximum is reached, old
250    * files start to be deleted to make room for new ones.
251    *
252    * Since: 0.10.31
253    */
254   g_object_class_install_property (gobject_class, PROP_MAX_FILES,
255       g_param_spec_uint ("max-files", "Max files",
256           "Maximum number of files to keep on disk. Once the maximum is reached,"
257           "old files start to be deleted to make room for new ones.",
258           0, G_MAXUINT, DEFAULT_MAX_FILES,
259           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
260
261   gobject_class->finalize = gst_multi_file_sink_finalize;
262
263   gstbasesink_class->get_times = NULL;
264   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
265   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
266   gstbasesink_class->set_caps =
267       GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
268 }
269
270 static void
271 gst_multi_file_sink_init (GstMultiFileSink * multifilesink,
272     GstMultiFileSinkClass * g_class)
273 {
274   multifilesink->filename = g_strdup (DEFAULT_LOCATION);
275   multifilesink->index = DEFAULT_INDEX;
276   multifilesink->post_messages = DEFAULT_POST_MESSAGES;
277   multifilesink->max_files = DEFAULT_MAX_FILES;
278   multifilesink->files = NULL;
279   multifilesink->n_files = 0;
280
281   gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
282
283   multifilesink->next_segment = GST_CLOCK_TIME_NONE;
284 }
285
286 static void
287 gst_multi_file_sink_finalize (GObject * object)
288 {
289   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
290
291   g_free (sink->filename);
292   g_slist_foreach (sink->files, (GFunc) g_free, NULL);
293   g_slist_free (sink->files);
294
295   G_OBJECT_CLASS (parent_class)->finalize (object);
296 }
297
298 static gboolean
299 gst_multi_file_sink_set_location (GstMultiFileSink * sink,
300     const gchar * location)
301 {
302   g_free (sink->filename);
303   /* FIXME: validate location to have just one %d */
304   sink->filename = g_strdup (location);
305
306   return TRUE;
307 }
308
309 static void
310 gst_multi_file_sink_set_property (GObject * object, guint prop_id,
311     const GValue * value, GParamSpec * pspec)
312 {
313   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
314
315   switch (prop_id) {
316     case PROP_LOCATION:
317       gst_multi_file_sink_set_location (sink, g_value_get_string (value));
318       break;
319     case PROP_INDEX:
320       sink->index = g_value_get_int (value);
321       break;
322     case PROP_POST_MESSAGES:
323       sink->post_messages = g_value_get_boolean (value);
324       break;
325     case PROP_NEXT_FILE:
326       sink->next_file = g_value_get_enum (value);
327       break;
328     case PROP_MAX_FILES:
329       sink->max_files = g_value_get_uint (value);
330       break;
331     default:
332       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
333       break;
334   }
335 }
336
337 static void
338 gst_multi_file_sink_get_property (GObject * object, guint prop_id,
339     GValue * value, GParamSpec * pspec)
340 {
341   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
342
343   switch (prop_id) {
344     case PROP_LOCATION:
345       g_value_set_string (value, sink->filename);
346       break;
347     case PROP_INDEX:
348       g_value_set_int (value, sink->index);
349       break;
350     case PROP_POST_MESSAGES:
351       g_value_set_boolean (value, sink->post_messages);
352       break;
353     case PROP_NEXT_FILE:
354       g_value_set_enum (value, sink->next_file);
355       break;
356     case PROP_MAX_FILES:
357       g_value_set_uint (value, sink->max_files);
358       break;
359     default:
360       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
361       break;
362   }
363 }
364
365 static gboolean
366 gst_multi_file_sink_stop (GstBaseSink * sink)
367 {
368   GstMultiFileSink *multifilesink;
369   int i;
370
371   multifilesink = GST_MULTI_FILE_SINK (sink);
372
373   if (multifilesink->file != NULL) {
374     fclose (multifilesink->file);
375     multifilesink->file = NULL;
376   }
377
378   if (multifilesink->streamheaders) {
379     for (i = 0; i < multifilesink->n_streamheaders; i++) {
380       gst_buffer_unref (multifilesink->streamheaders[i]);
381     }
382     g_free (multifilesink->streamheaders);
383     multifilesink->streamheaders = NULL;
384   }
385
386   return TRUE;
387 }
388
389
390 static void
391 gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
392     GstBuffer * buffer, const char *filename)
393 {
394   if (multifilesink->post_messages) {
395     GstClockTime duration, timestamp;
396     GstClockTime running_time, stream_time;
397     guint64 offset, offset_end;
398     GstStructure *s;
399     GstSegment *segment;
400     GstFormat format;
401
402     segment = &GST_BASE_SINK (multifilesink)->segment;
403     format = segment->format;
404
405     timestamp = GST_BUFFER_TIMESTAMP (buffer);
406     duration = GST_BUFFER_DURATION (buffer);
407     offset = GST_BUFFER_OFFSET (buffer);
408     offset_end = GST_BUFFER_OFFSET_END (buffer);
409
410     running_time = gst_segment_to_running_time (segment, format, timestamp);
411     stream_time = gst_segment_to_stream_time (segment, format, timestamp);
412
413     s = gst_structure_new ("GstMultiFileSink",
414         "filename", G_TYPE_STRING, filename,
415         "index", G_TYPE_INT, multifilesink->index,
416         "timestamp", G_TYPE_UINT64, timestamp,
417         "stream-time", G_TYPE_UINT64, stream_time,
418         "running-time", G_TYPE_UINT64, running_time,
419         "duration", G_TYPE_UINT64, duration,
420         "offset", G_TYPE_UINT64, offset,
421         "offset-end", G_TYPE_UINT64, offset_end, NULL);
422
423     gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
424         gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
425   }
426 }
427
428 static GstFlowReturn
429 gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
430 {
431   GstMultiFileSink *multifilesink;
432   guint size;
433   guint8 *data;
434   gchar *filename;
435   gboolean ret;
436   GError *error = NULL;
437
438   size = GST_BUFFER_SIZE (buffer);
439   data = GST_BUFFER_DATA (buffer);
440
441   multifilesink = GST_MULTI_FILE_SINK (sink);
442
443   switch (multifilesink->next_file) {
444     case GST_MULTI_FILE_SINK_NEXT_BUFFER:
445       gst_multi_file_sink_ensure_max_files (multifilesink);
446
447       filename = g_strdup_printf (multifilesink->filename,
448           multifilesink->index);
449       ret = g_file_set_contents (filename, (char *) data, size, &error);
450       if (!ret)
451         goto write_error;
452
453       multifilesink->files = g_slist_append (multifilesink->files, filename);
454       multifilesink->n_files += 1;
455
456       gst_multi_file_sink_post_message (multifilesink, buffer, filename);
457       multifilesink->index++;
458
459       break;
460     case GST_MULTI_FILE_SINK_NEXT_DISCONT:
461       if (GST_BUFFER_IS_DISCONT (buffer)) {
462         if (multifilesink->file)
463           gst_multi_file_sink_close_file (multifilesink, buffer);
464       }
465
466       if (multifilesink->file == NULL) {
467         if (!gst_multi_file_sink_open_next_file (multifilesink))
468           goto stdio_write_error;
469       }
470
471       ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
472           multifilesink->file);
473       if (ret != 1)
474         goto stdio_write_error;
475
476       break;
477     case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
478       if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
479         if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
480           multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
481               10 * GST_SECOND;
482         }
483       }
484
485       if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
486           GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
487           !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
488         if (multifilesink->file)
489           gst_multi_file_sink_close_file (multifilesink, buffer);
490
491         multifilesink->next_segment += 10 * GST_SECOND;
492       }
493
494       if (multifilesink->file == NULL) {
495         int i;
496
497         if (!gst_multi_file_sink_open_next_file (multifilesink))
498           goto stdio_write_error;
499
500         if (multifilesink->streamheaders) {
501           for (i = 0; i < multifilesink->n_streamheaders; i++) {
502             ret = fwrite (GST_BUFFER_DATA (multifilesink->streamheaders[i]),
503                 GST_BUFFER_SIZE (multifilesink->streamheaders[i]), 1,
504                 multifilesink->file);
505             if (ret != 1)
506               goto stdio_write_error;
507           }
508         }
509       }
510
511       ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
512           multifilesink->file);
513       if (ret != 1)
514         goto stdio_write_error;
515
516       break;
517     default:
518       g_assert_not_reached ();
519   }
520
521   return GST_FLOW_OK;
522
523   /* ERRORS */
524 write_error:
525   {
526     switch (error->code) {
527       case G_FILE_ERROR_NOSPC:{
528         GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
529             (NULL));
530         break;
531       }
532       default:{
533         GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
534             ("Error while writing to file \"%s\".", filename),
535             ("%s", g_strerror (errno)));
536       }
537     }
538     g_error_free (error);
539     g_free (filename);
540
541     return GST_FLOW_ERROR;
542   }
543 stdio_write_error:
544   GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
545       ("Error while writing to file."), (NULL));
546   return GST_FLOW_ERROR;
547 }
548
549 static gboolean
550 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
551 {
552   GstMultiFileSink *multifilesink;
553   GstStructure *structure;
554
555   multifilesink = GST_MULTI_FILE_SINK (sink);
556
557   structure = gst_caps_get_structure (caps, 0);
558   if (structure) {
559     const GValue *value;
560
561     value = gst_structure_get_value (structure, "streamheader");
562
563     if (GST_VALUE_HOLDS_ARRAY (value)) {
564       int i;
565
566       if (multifilesink->streamheaders) {
567         for (i = 0; i < multifilesink->n_streamheaders; i++) {
568           gst_buffer_unref (multifilesink->streamheaders[i]);
569         }
570         g_free (multifilesink->streamheaders);
571       }
572
573       multifilesink->n_streamheaders = gst_value_array_get_size (value);
574       multifilesink->streamheaders =
575           g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
576
577       for (i = 0; i < multifilesink->n_streamheaders; i++) {
578         multifilesink->streamheaders[i] =
579             gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value
580                 (value, i)));
581       }
582     }
583   }
584
585   return TRUE;
586 }
587
588 static void
589 gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
590 {
591   char *filename;
592
593   while (multifilesink->max_files &&
594       multifilesink->n_files >= multifilesink->max_files) {
595     filename = multifilesink->files->data;
596     g_remove (filename);
597     g_free (filename);
598     multifilesink->files = g_slist_delete_link (multifilesink->files,
599         multifilesink->files);
600     multifilesink->n_files -= 1;
601   }
602 }
603
604 static gboolean
605 gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
606 {
607   char *filename;
608
609   g_return_val_if_fail (multifilesink->file == NULL, FALSE);
610
611   gst_multi_file_sink_ensure_max_files (multifilesink);
612   filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
613   multifilesink->file = g_fopen (filename, "wb");
614   if (multifilesink->file == NULL) {
615     g_free (filename);
616     return FALSE;
617   }
618
619   multifilesink->files = g_slist_append (multifilesink->files, filename);
620   multifilesink->n_files += 1;
621
622   return TRUE;
623 }
624
625 static void
626 gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
627     GstBuffer * buffer)
628 {
629   char *filename;
630
631   fclose (multifilesink->file);
632   multifilesink->file = NULL;
633
634   if (buffer) {
635     filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
636     gst_multi_file_sink_post_message (multifilesink, buffer, filename);
637     g_free (filename);
638   }
639
640   multifilesink->index++;
641 }