upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.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
129 enum
130 {
131   PROP_0,
132   PROP_LOCATION,
133   PROP_INDEX,
134   PROP_POST_MESSAGES,
135   PROP_NEXT_FILE,
136   PROP_LAST
137 };
138
139 static void gst_multi_file_sink_finalize (GObject * object);
140
141 static void gst_multi_file_sink_set_property (GObject * object, guint prop_id,
142     const GValue * value, GParamSpec * pspec);
143 static void gst_multi_file_sink_get_property (GObject * object, guint prop_id,
144     GValue * value, GParamSpec * pspec);
145
146 static gboolean gst_multi_file_sink_stop (GstBaseSink * sink);
147 static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
148     GstBuffer * buffer);
149 static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink,
150     GstCaps * caps);
151
152 #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
153 static GType
154 gst_multi_file_sink_next_get_type (void)
155 {
156   static GType multi_file_sync_next_type = 0;
157   static const GEnumValue next_types[] = {
158     {GST_MULTI_FILE_SINK_NEXT_BUFFER, "New file for each buffer", "buffer"},
159     {GST_MULTI_FILE_SINK_NEXT_DISCONT, "New file after each discontinuity",
160         "discont"},
161     {GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, "New file at each key frame "
162           "(Useful for MPEG-TS segmenting)", "key-frame"},
163     {0, NULL, NULL}
164   };
165
166   if (!multi_file_sync_next_type) {
167     multi_file_sync_next_type =
168         g_enum_register_static ("GstMultiFileSinkNext", next_types);
169   }
170
171   return multi_file_sync_next_type;
172 }
173
174 GST_BOILERPLATE (GstMultiFileSink, gst_multi_file_sink, GstBaseSink,
175     GST_TYPE_BASE_SINK);
176
177 static void
178 gst_multi_file_sink_base_init (gpointer g_class)
179 {
180   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
181
182   GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
183       "multifilesink element");
184
185   gst_element_class_add_pad_template (gstelement_class,
186       gst_static_pad_template_get (&sinktemplate));
187   gst_element_class_set_details_simple (gstelement_class, "Multi-File Sink",
188       "Sink/File",
189       "Write buffers to a sequentially named set of files",
190       "David Schleef <ds@schleef.org>");
191 }
192
193 static void
194 gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
195 {
196   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
197   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
198
199   gobject_class->set_property = gst_multi_file_sink_set_property;
200   gobject_class->get_property = gst_multi_file_sink_get_property;
201
202   g_object_class_install_property (gobject_class, PROP_LOCATION,
203       g_param_spec_string ("location", "File Location",
204           "Location of the file to write", NULL,
205           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
206
207   g_object_class_install_property (gobject_class, PROP_INDEX,
208       g_param_spec_int ("index", "Index",
209           "Index to use with location property to create file names.  The "
210           "index is incremented by one for each buffer written.",
211           0, G_MAXINT, DEFAULT_INDEX,
212           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
213   /**
214    * GstMultiFileSink:post-messages
215    *
216    * Post a message on the GstBus for each file.
217    *
218    * Since: 0.10.17
219    */
220   g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
221       g_param_spec_boolean ("post-messages", "Post Messages",
222           "Post a message for each file with information of the buffer",
223           DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
224   /**
225    * GstMultiFileSink:next-file
226    *
227    * When to start a new file.
228    *
229    * Since: 0.10.17
230    */
231   g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
232       g_param_spec_enum ("next-file", "Next File",
233           "When to start a new file",
234           GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
235           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
236
237   gobject_class->finalize = gst_multi_file_sink_finalize;
238
239   gstbasesink_class->get_times = NULL;
240   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
241   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
242   gstbasesink_class->set_caps =
243       GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
244 }
245
246 static void
247 gst_multi_file_sink_init (GstMultiFileSink * multifilesink,
248     GstMultiFileSinkClass * g_class)
249 {
250   multifilesink->filename = g_strdup (DEFAULT_LOCATION);
251   multifilesink->index = DEFAULT_INDEX;
252   multifilesink->post_messages = DEFAULT_POST_MESSAGES;
253
254   gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
255
256   multifilesink->next_segment = GST_CLOCK_TIME_NONE;
257 }
258
259 static void
260 gst_multi_file_sink_finalize (GObject * object)
261 {
262   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
263
264   g_free (sink->filename);
265
266   G_OBJECT_CLASS (parent_class)->finalize (object);
267 }
268
269 static gboolean
270 gst_multi_file_sink_set_location (GstMultiFileSink * sink,
271     const gchar * location)
272 {
273   g_free (sink->filename);
274   /* FIXME: validate location to have just one %d */
275   sink->filename = g_strdup (location);
276
277   return TRUE;
278 }
279
280 static void
281 gst_multi_file_sink_set_property (GObject * object, guint prop_id,
282     const GValue * value, GParamSpec * pspec)
283 {
284   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
285
286   switch (prop_id) {
287     case PROP_LOCATION:
288       gst_multi_file_sink_set_location (sink, g_value_get_string (value));
289       break;
290     case PROP_INDEX:
291       sink->index = g_value_get_int (value);
292       break;
293     case PROP_POST_MESSAGES:
294       sink->post_messages = g_value_get_boolean (value);
295       break;
296     case PROP_NEXT_FILE:
297       sink->next_file = g_value_get_enum (value);
298       break;
299     default:
300       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
301       break;
302   }
303 }
304
305 static void
306 gst_multi_file_sink_get_property (GObject * object, guint prop_id,
307     GValue * value, GParamSpec * pspec)
308 {
309   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
310
311   switch (prop_id) {
312     case PROP_LOCATION:
313       g_value_set_string (value, sink->filename);
314       break;
315     case PROP_INDEX:
316       g_value_set_int (value, sink->index);
317       break;
318     case PROP_POST_MESSAGES:
319       g_value_set_boolean (value, sink->post_messages);
320       break;
321     case PROP_NEXT_FILE:
322       g_value_set_enum (value, sink->next_file);
323       break;
324     default:
325       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
326       break;
327   }
328 }
329
330 static gboolean
331 gst_multi_file_sink_stop (GstBaseSink * sink)
332 {
333   GstMultiFileSink *multifilesink;
334   int i;
335
336   multifilesink = GST_MULTI_FILE_SINK (sink);
337
338   if (multifilesink->file != NULL) {
339     fclose (multifilesink->file);
340     multifilesink->file = NULL;
341   }
342
343   if (multifilesink->streamheaders) {
344     for (i = 0; i < multifilesink->n_streamheaders; i++) {
345       gst_buffer_unref (multifilesink->streamheaders[i]);
346     }
347     g_free (multifilesink->streamheaders);
348   }
349
350   return TRUE;
351 }
352
353
354 static void
355 gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
356     GstBuffer * buffer, const char *filename)
357 {
358   if (multifilesink->post_messages) {
359     GstClockTime duration, timestamp;
360     GstClockTime running_time, stream_time;
361     guint64 offset, offset_end;
362     GstStructure *s;
363     GstSegment *segment;
364     GstFormat format;
365
366     segment = &GST_BASE_SINK (multifilesink)->segment;
367     format = segment->format;
368
369     timestamp = GST_BUFFER_TIMESTAMP (buffer);
370     duration = GST_BUFFER_DURATION (buffer);
371     offset = GST_BUFFER_OFFSET (buffer);
372     offset_end = GST_BUFFER_OFFSET_END (buffer);
373
374     running_time = gst_segment_to_running_time (segment, format, timestamp);
375     stream_time = gst_segment_to_stream_time (segment, format, timestamp);
376
377     s = gst_structure_new ("GstMultiFileSink",
378         "filename", G_TYPE_STRING, filename,
379         "index", G_TYPE_INT, multifilesink->index,
380         "timestamp", G_TYPE_UINT64, timestamp,
381         "stream-time", G_TYPE_UINT64, stream_time,
382         "running-time", G_TYPE_UINT64, running_time,
383         "duration", G_TYPE_UINT64, duration,
384         "offset", G_TYPE_UINT64, offset,
385         "offset-end", G_TYPE_UINT64, offset_end, NULL);
386
387     gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
388         gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
389   }
390 }
391
392 static GstFlowReturn
393 gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
394 {
395   GstMultiFileSink *multifilesink;
396   guint size;
397   guint8 *data;
398   gchar *filename;
399   gboolean ret;
400   GError *error = NULL;
401
402   size = GST_BUFFER_SIZE (buffer);
403   data = GST_BUFFER_DATA (buffer);
404
405   multifilesink = GST_MULTI_FILE_SINK (sink);
406
407   switch (multifilesink->next_file) {
408     case GST_MULTI_FILE_SINK_NEXT_BUFFER:
409       filename = g_strdup_printf (multifilesink->filename,
410           multifilesink->index);
411
412       ret = g_file_set_contents (filename, (char *) data, size, &error);
413       if (!ret)
414         goto write_error;
415
416       gst_multi_file_sink_post_message (multifilesink, buffer, filename);
417       multifilesink->index++;
418
419       g_free (filename);
420       break;
421     case GST_MULTI_FILE_SINK_NEXT_DISCONT:
422       if (GST_BUFFER_IS_DISCONT (buffer)) {
423         if (multifilesink->file) {
424           fclose (multifilesink->file);
425           multifilesink->file = NULL;
426
427           filename = g_strdup_printf (multifilesink->filename,
428               multifilesink->index);
429           gst_multi_file_sink_post_message (multifilesink, buffer, filename);
430           g_free (filename);
431           multifilesink->index++;
432         }
433       }
434
435       if (multifilesink->file == NULL) {
436         filename = g_strdup_printf (multifilesink->filename,
437             multifilesink->index);
438         multifilesink->file = g_fopen (filename, "wb");
439         g_free (filename);
440
441         if (multifilesink->file == NULL)
442           goto stdio_write_error;
443       }
444
445       ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
446           multifilesink->file);
447       if (ret != 1)
448         goto stdio_write_error;
449
450       break;
451     case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
452       if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
453         if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
454           multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
455               10 * GST_SECOND;
456         }
457       }
458
459       if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
460           GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
461           !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
462         if (multifilesink->file) {
463           fclose (multifilesink->file);
464           multifilesink->file = NULL;
465
466           filename = g_strdup_printf (multifilesink->filename,
467               multifilesink->index);
468           gst_multi_file_sink_post_message (multifilesink, buffer, filename);
469           g_free (filename);
470           multifilesink->index++;
471         }
472
473         multifilesink->next_segment += 10 * GST_SECOND;
474       }
475
476       if (multifilesink->file == NULL) {
477         int i;
478
479         filename = g_strdup_printf (multifilesink->filename,
480             multifilesink->index);
481         multifilesink->file = g_fopen (filename, "wb");
482         g_free (filename);
483
484         if (multifilesink->file == NULL)
485           goto stdio_write_error;
486
487         if (multifilesink->streamheaders) {
488           for (i = 0; i < multifilesink->n_streamheaders; i++) {
489             ret = fwrite (GST_BUFFER_DATA (multifilesink->streamheaders[i]),
490                 GST_BUFFER_SIZE (multifilesink->streamheaders[i]), 1,
491                 multifilesink->file);
492             if (ret != 1)
493               goto stdio_write_error;
494           }
495         }
496       }
497
498       ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
499           multifilesink->file);
500       if (ret != 1)
501         goto stdio_write_error;
502
503       break;
504     default:
505       g_assert_not_reached ();
506   }
507
508   return GST_FLOW_OK;
509
510   /* ERRORS */
511 write_error:
512   {
513     switch (error->code) {
514       case G_FILE_ERROR_NOSPC:{
515         GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
516             (NULL));
517         break;
518       }
519       default:{
520         GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
521             ("Error while writing to file \"%s\".", filename),
522             ("%s", g_strerror (errno)));
523       }
524     }
525     g_error_free (error);
526     g_free (filename);
527
528     return GST_FLOW_ERROR;
529   }
530 stdio_write_error:
531   GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
532       ("Error while writing to file."), (NULL));
533   return GST_FLOW_ERROR;
534 }
535
536 static gboolean
537 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
538 {
539   GstMultiFileSink *multifilesink;
540   GstStructure *structure;
541
542   multifilesink = GST_MULTI_FILE_SINK (sink);
543
544   structure = gst_caps_get_structure (caps, 0);
545   if (structure) {
546     const GValue *value;
547
548     value = gst_structure_get_value (structure, "streamheader");
549
550     if (GST_VALUE_HOLDS_ARRAY (value)) {
551       int i;
552
553       if (multifilesink->streamheaders) {
554         for (i = 0; i < multifilesink->n_streamheaders; i++) {
555           gst_buffer_unref (multifilesink->streamheaders[i]);
556         }
557         g_free (multifilesink->streamheaders);
558       }
559
560       multifilesink->n_streamheaders = gst_value_array_get_size (value);
561       multifilesink->streamheaders =
562           g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
563
564       for (i = 0; i < multifilesink->n_streamheaders; i++) {
565         multifilesink->streamheaders[i] =
566             gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value
567                 (value, i)));
568       }
569     }
570   }
571
572   return TRUE;
573 }