Tizen 2.0 Release
[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  *                    2011 Collabora Ltd. <tim.muller@collabora.co.uk>
7  *
8  * gstmultifilesink.c:
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25 /**
26  * SECTION:element-multifilesink
27  * @see_also: #GstFileSrc
28  *
29  * Write incoming data to a series of sequentially-named files.
30  *
31  * The filename property should contain a string with a \%d placeholder that will
32  * be substituted with the index for each filename.
33  *
34  * If the #GstMultiFileSink:post-messages property is #TRUE, it sends an application
35  * message named
36  * <classname>&quot;GstMultiFileSink&quot;</classname> after writing each
37  * buffer.
38  *
39  * The message's structure contains these fields:
40  * <itemizedlist>
41  * <listitem>
42  *   <para>
43  *   #gchar *
44  *   <classname>&quot;filename&quot;</classname>:
45  *   the filename where the buffer was written.
46  *   </para>
47  * </listitem>
48  * <listitem>
49  *   <para>
50  *   #gint
51  *   <classname>&quot;index&quot;</classname>:
52  *   the index of the buffer.
53  *   </para>
54  * </listitem>
55  * <listitem>
56  *   <para>
57  *   #GstClockTime
58  *   <classname>&quot;timestamp&quot;</classname>:
59  *   the timestamp of the buffer.
60  *   </para>
61  * </listitem>
62  * <listitem>
63  *   <para>
64  *   #GstClockTime
65  *   <classname>&quot;stream-time&quot;</classname>:
66  *   the stream time of the buffer.
67  *   </para>
68  * </listitem>
69  * <listitem>
70  *   <para>
71  *   #GstClockTime
72  *   <classname>&quot;running-time&quot;</classname>:
73  *   the running_time of the buffer.
74  *   </para>
75  * </listitem>
76  * <listitem>
77  *   <para>
78  *   #GstClockTime
79  *   <classname>&quot;duration&quot;</classname>:
80  *   the duration of the buffer.
81  *   </para>
82  * </listitem>
83  * <listitem>
84  *   <para>
85  *   #guint64
86  *   <classname>&quot;offset&quot;</classname>:
87  *   the offset of the buffer that triggered the message.
88  *   </para>
89  * </listitem>
90  * <listitem>
91  *   <para>
92  *   #guint64
93  *   <classname>&quot;offset-end&quot;</classname>:
94  *   the offset-end of the buffer that triggered the message.
95  *   </para>
96  * </listitem>
97  * </itemizedlist>
98  *
99  * <refsect2>
100  * <title>Example launch line</title>
101  * |[
102  * gst-launch audiotestsrc ! multifilesink
103  * gst-launch videotestsrc ! multifilesink post-messages=true filename="frame%d"
104  * ]|
105  * </refsect2>
106  *
107  * Last reviewed on 2009-09-11 (0.10.17)
108  */
109
110 #ifdef HAVE_CONFIG_H
111 #  include "config.h"
112 #endif
113 #include <gst/base/gstbasetransform.h>
114 #include <gst/video/video.h>
115 #include <glib/gstdio.h>
116 #include "gstmultifilesink.h"
117
118 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
119     GST_PAD_SINK,
120     GST_PAD_ALWAYS,
121     GST_STATIC_CAPS_ANY);
122
123 GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug);
124 #define GST_CAT_DEFAULT gst_multi_file_sink_debug
125
126 #define DEFAULT_LOCATION "%05d"
127 #define DEFAULT_INDEX 0
128 #define DEFAULT_POST_MESSAGES FALSE
129 #define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER
130 #define DEFAULT_MAX_FILES 0
131 #define DEFAULT_MAX_FILE_SIZE G_GUINT64_CONSTANT(2*1024*1024*1024)
132
133 enum
134 {
135   PROP_0,
136   PROP_LOCATION,
137   PROP_INDEX,
138   PROP_POST_MESSAGES,
139   PROP_NEXT_FILE,
140   PROP_MAX_FILES,
141   PROP_MAX_FILE_SIZE,
142   PROP_LAST
143 };
144
145 static void gst_multi_file_sink_finalize (GObject * object);
146
147 static void gst_multi_file_sink_set_property (GObject * object, guint prop_id,
148     const GValue * value, GParamSpec * pspec);
149 static void gst_multi_file_sink_get_property (GObject * object, guint prop_id,
150     GValue * value, GParamSpec * pspec);
151
152 static gboolean gst_multi_file_sink_stop (GstBaseSink * sink);
153 static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
154     GstBuffer * buffer);
155 static GstFlowReturn gst_multi_file_sink_render_list (GstBaseSink * sink,
156     GstBufferList * buffer_list);
157 static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink,
158     GstCaps * caps);
159 static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink *
160     multifilesink);
161 static void gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
162     GstBuffer * buffer);
163 static void gst_multi_file_sink_ensure_max_files (GstMultiFileSink *
164     multifilesink);
165 static gboolean gst_multi_file_sink_event (GstBaseSink * sink,
166     GstEvent * event);
167
168 #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
169 static GType
170 gst_multi_file_sink_next_get_type (void)
171 {
172   static GType multi_file_sync_next_type = 0;
173   static const GEnumValue next_types[] = {
174     {GST_MULTI_FILE_SINK_NEXT_BUFFER, "New file for each buffer", "buffer"},
175     {GST_MULTI_FILE_SINK_NEXT_DISCONT, "New file after each discontinuity",
176         "discont"},
177     {GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, "New file at each key frame "
178           "(Useful for MPEG-TS segmenting)", "key-frame"},
179     {GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT,
180         "New file after a force key unit event", "key-unit-event"},
181     {GST_MULTI_FILE_SINK_NEXT_MAX_SIZE, "New file when the configured maximum "
182           "file size would be exceeded with the next buffer or buffer list",
183         "max-size"},
184     {0, NULL, NULL}
185   };
186
187   if (!multi_file_sync_next_type) {
188     multi_file_sync_next_type =
189         g_enum_register_static ("GstMultiFileSinkNext", next_types);
190   }
191
192   return multi_file_sync_next_type;
193 }
194
195 GST_BOILERPLATE (GstMultiFileSink, gst_multi_file_sink, GstBaseSink,
196     GST_TYPE_BASE_SINK);
197
198 static void
199 gst_multi_file_sink_base_init (gpointer g_class)
200 {
201   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
202
203   GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
204       "multifilesink element");
205
206   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
207   gst_element_class_set_details_simple (gstelement_class, "Multi-File Sink",
208       "Sink/File",
209       "Write buffers to a sequentially named set of files",
210       "David Schleef <ds@schleef.org>");
211 }
212
213 static void
214 gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
215 {
216   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
217   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
218
219   gobject_class->set_property = gst_multi_file_sink_set_property;
220   gobject_class->get_property = gst_multi_file_sink_get_property;
221
222   g_object_class_install_property (gobject_class, PROP_LOCATION,
223       g_param_spec_string ("location", "File Location",
224           "Location of the file to write", NULL,
225           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
226
227   g_object_class_install_property (gobject_class, PROP_INDEX,
228       g_param_spec_int ("index", "Index",
229           "Index to use with location property to create file names.  The "
230           "index is incremented by one for each buffer written.",
231           0, G_MAXINT, DEFAULT_INDEX,
232           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
233   /**
234    * GstMultiFileSink:post-messages
235    *
236    * Post a message on the GstBus for each file.
237    *
238    * Since: 0.10.17
239    */
240   g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
241       g_param_spec_boolean ("post-messages", "Post Messages",
242           "Post a message for each file with information of the buffer",
243           DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
244   /**
245    * GstMultiFileSink:next-file
246    *
247    * When to start a new file.
248    *
249    * Since: 0.10.17
250    */
251   g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
252       g_param_spec_enum ("next-file", "Next File",
253           "When to start a new file",
254           GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
255           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
256
257
258   /**
259    * GstMultiFileSink:max-files
260    *
261    * Maximum number of files to keep on disk. Once the maximum is reached, old
262    * files start to be deleted to make room for new ones.
263    *
264    * Since: 0.10.31
265    */
266   g_object_class_install_property (gobject_class, PROP_MAX_FILES,
267       g_param_spec_uint ("max-files", "Max files",
268           "Maximum number of files to keep on disk. Once the maximum is reached,"
269           "old files start to be deleted to make room for new ones.",
270           0, G_MAXUINT, DEFAULT_MAX_FILES,
271           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
272
273   /**
274    * GstMultiFileSink:max-file-size
275    *
276    * Maximum file size before starting a new file in max-size mode.
277    *
278    * Since: 0.10.31
279    */
280   g_object_class_install_property (gobject_class, PROP_MAX_FILE_SIZE,
281       g_param_spec_uint64 ("max-file-size", "Maximum File Size",
282           "Maximum file size before starting a new file in max-size mode",
283           0, G_MAXUINT64, DEFAULT_MAX_FILE_SIZE,
284           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
285
286   gobject_class->finalize = gst_multi_file_sink_finalize;
287
288   gstbasesink_class->get_times = NULL;
289   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
290   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
291   gstbasesink_class->render_list =
292       GST_DEBUG_FUNCPTR (gst_multi_file_sink_render_list);
293   gstbasesink_class->set_caps =
294       GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
295   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_multi_file_sink_event);
296 }
297
298 static void
299 gst_multi_file_sink_init (GstMultiFileSink * multifilesink,
300     GstMultiFileSinkClass * g_class)
301 {
302   multifilesink->filename = g_strdup (DEFAULT_LOCATION);
303   multifilesink->index = DEFAULT_INDEX;
304   multifilesink->post_messages = DEFAULT_POST_MESSAGES;
305   multifilesink->max_files = DEFAULT_MAX_FILES;
306   multifilesink->max_file_size = DEFAULT_MAX_FILE_SIZE;
307   multifilesink->files = NULL;
308   multifilesink->n_files = 0;
309
310   gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
311
312   multifilesink->next_segment = GST_CLOCK_TIME_NONE;
313   multifilesink->force_key_unit_count = -1;
314 }
315
316 static void
317 gst_multi_file_sink_finalize (GObject * object)
318 {
319   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
320
321   g_free (sink->filename);
322   g_slist_foreach (sink->files, (GFunc) g_free, NULL);
323   g_slist_free (sink->files);
324
325   G_OBJECT_CLASS (parent_class)->finalize (object);
326 }
327
328 static gboolean
329 gst_multi_file_sink_set_location (GstMultiFileSink * sink,
330     const gchar * location)
331 {
332   g_free (sink->filename);
333   /* FIXME: validate location to have just one %d */
334   sink->filename = g_strdup (location);
335
336   return TRUE;
337 }
338
339 static void
340 gst_multi_file_sink_set_property (GObject * object, guint prop_id,
341     const GValue * value, GParamSpec * pspec)
342 {
343   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
344
345   switch (prop_id) {
346     case PROP_LOCATION:
347       gst_multi_file_sink_set_location (sink, g_value_get_string (value));
348       break;
349     case PROP_INDEX:
350       sink->index = g_value_get_int (value);
351       break;
352     case PROP_POST_MESSAGES:
353       sink->post_messages = g_value_get_boolean (value);
354       break;
355     case PROP_NEXT_FILE:
356       sink->next_file = g_value_get_enum (value);
357       break;
358     case PROP_MAX_FILES:
359       sink->max_files = g_value_get_uint (value);
360       break;
361     case PROP_MAX_FILE_SIZE:
362       sink->max_file_size = g_value_get_uint64 (value);
363       break;
364     default:
365       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
366       break;
367   }
368 }
369
370 static void
371 gst_multi_file_sink_get_property (GObject * object, guint prop_id,
372     GValue * value, GParamSpec * pspec)
373 {
374   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
375
376   switch (prop_id) {
377     case PROP_LOCATION:
378       g_value_set_string (value, sink->filename);
379       break;
380     case PROP_INDEX:
381       g_value_set_int (value, sink->index);
382       break;
383     case PROP_POST_MESSAGES:
384       g_value_set_boolean (value, sink->post_messages);
385       break;
386     case PROP_NEXT_FILE:
387       g_value_set_enum (value, sink->next_file);
388       break;
389     case PROP_MAX_FILES:
390       g_value_set_uint (value, sink->max_files);
391       break;
392     case PROP_MAX_FILE_SIZE:
393       g_value_set_uint64 (value, sink->max_file_size);
394       break;
395     default:
396       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
397       break;
398   }
399 }
400
401 static gboolean
402 gst_multi_file_sink_stop (GstBaseSink * sink)
403 {
404   GstMultiFileSink *multifilesink;
405   int i;
406
407   multifilesink = GST_MULTI_FILE_SINK (sink);
408
409   if (multifilesink->file != NULL) {
410     fclose (multifilesink->file);
411     multifilesink->file = NULL;
412   }
413
414   if (multifilesink->streamheaders) {
415     for (i = 0; i < multifilesink->n_streamheaders; i++) {
416       gst_buffer_unref (multifilesink->streamheaders[i]);
417     }
418     g_free (multifilesink->streamheaders);
419     multifilesink->streamheaders = NULL;
420   }
421
422   multifilesink->force_key_unit_count = -1;
423
424   return TRUE;
425 }
426
427
428 static void
429 gst_multi_file_sink_post_message_full (GstMultiFileSink * multifilesink,
430     GstClockTime timestamp, GstClockTime duration, GstClockTime offset,
431     GstClockTime offset_end, GstClockTime running_time,
432     GstClockTime stream_time, const char *filename)
433 {
434   GstStructure *s;
435
436   if (!multifilesink->post_messages)
437     return;
438
439   s = gst_structure_new ("GstMultiFileSink",
440       "filename", G_TYPE_STRING, filename,
441       "index", G_TYPE_INT, multifilesink->index,
442       "timestamp", G_TYPE_UINT64, timestamp,
443       "stream-time", G_TYPE_UINT64, stream_time,
444       "running-time", G_TYPE_UINT64, running_time,
445       "duration", G_TYPE_UINT64, duration,
446       "offset", G_TYPE_UINT64, offset,
447       "offset-end", G_TYPE_UINT64, offset_end, NULL);
448
449   gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
450       gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
451 }
452
453
454 static void
455 gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
456     GstBuffer * buffer, const char *filename)
457 {
458   GstClockTime duration, timestamp;
459   GstClockTime running_time, stream_time;
460   guint64 offset, offset_end;
461   GstSegment *segment;
462   GstFormat format;
463
464   if (!multifilesink->post_messages)
465     return;
466
467   segment = &GST_BASE_SINK (multifilesink)->segment;
468   format = segment->format;
469
470   timestamp = GST_BUFFER_TIMESTAMP (buffer);
471   duration = GST_BUFFER_DURATION (buffer);
472   offset = GST_BUFFER_OFFSET (buffer);
473   offset_end = GST_BUFFER_OFFSET_END (buffer);
474
475   running_time = gst_segment_to_running_time (segment, format, timestamp);
476   stream_time = gst_segment_to_stream_time (segment, format, timestamp);
477
478   gst_multi_file_sink_post_message_full (multifilesink, timestamp, duration,
479       offset, offset_end, running_time, stream_time, filename);
480 }
481
482 static gboolean
483 gst_multi_file_sink_write_stream_headers (GstMultiFileSink * sink)
484 {
485   int i;
486
487   if (sink->streamheaders == NULL)
488     return TRUE;
489
490   /* we want to write these at the beginning */
491   g_assert (sink->cur_file_size == 0);
492
493   for (i = 0; i < sink->n_streamheaders; i++) {
494     GstBuffer *hdr;
495     int ret;
496
497     hdr = sink->streamheaders[i];
498
499     ret = fwrite (GST_BUFFER_DATA (hdr), GST_BUFFER_SIZE (hdr), 1, sink->file);
500
501     if (ret != 1)
502       return FALSE;
503
504     sink->cur_file_size += GST_BUFFER_SIZE (hdr);
505   }
506
507   return TRUE;
508 }
509
510 static GstFlowReturn
511 gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
512 {
513   GstMultiFileSink *multifilesink;
514   guint size;
515   guint8 *data;
516   gchar *filename;
517   gboolean ret;
518   GError *error = NULL;
519
520   size = GST_BUFFER_SIZE (buffer);
521   data = GST_BUFFER_DATA (buffer);
522
523   multifilesink = GST_MULTI_FILE_SINK (sink);
524
525   switch (multifilesink->next_file) {
526     case GST_MULTI_FILE_SINK_NEXT_BUFFER:
527       gst_multi_file_sink_ensure_max_files (multifilesink);
528
529       filename = g_strdup_printf (multifilesink->filename,
530           multifilesink->index);
531       ret = g_file_set_contents (filename, (char *) data, size, &error);
532       if (!ret)
533         goto write_error;
534
535       multifilesink->files = g_slist_append (multifilesink->files, filename);
536       multifilesink->n_files += 1;
537
538       gst_multi_file_sink_post_message (multifilesink, buffer, filename);
539       multifilesink->index++;
540
541       break;
542     case GST_MULTI_FILE_SINK_NEXT_DISCONT:
543       if (GST_BUFFER_IS_DISCONT (buffer)) {
544         if (multifilesink->file)
545           gst_multi_file_sink_close_file (multifilesink, buffer);
546       }
547
548       if (multifilesink->file == NULL) {
549         if (!gst_multi_file_sink_open_next_file (multifilesink))
550           goto stdio_write_error;
551       }
552
553       ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
554           multifilesink->file);
555       if (ret != 1)
556         goto stdio_write_error;
557
558       break;
559     case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
560       if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
561         if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
562           multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
563               10 * GST_SECOND;
564         }
565       }
566
567       if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
568           GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
569           !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
570         if (multifilesink->file)
571           gst_multi_file_sink_close_file (multifilesink, buffer);
572
573         multifilesink->next_segment += 10 * GST_SECOND;
574       }
575
576       if (multifilesink->file == NULL) {
577         if (!gst_multi_file_sink_open_next_file (multifilesink))
578           goto stdio_write_error;
579
580         gst_multi_file_sink_write_stream_headers (multifilesink);
581       }
582
583       ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
584           multifilesink->file);
585       if (ret != 1)
586         goto stdio_write_error;
587
588       break;
589     case GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT:
590       if (multifilesink->file == NULL) {
591         if (!gst_multi_file_sink_open_next_file (multifilesink))
592           goto stdio_write_error;
593       }
594
595       ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
596           multifilesink->file);
597       if (ret != 1)
598         goto stdio_write_error;
599
600       break;
601     case GST_MULTI_FILE_SINK_NEXT_MAX_SIZE:{
602       guint64 new_size;
603
604       new_size = multifilesink->cur_file_size + GST_BUFFER_SIZE (buffer);
605       if (new_size > multifilesink->max_file_size) {
606
607         GST_INFO_OBJECT (multifilesink, "current size: %" G_GUINT64_FORMAT
608             ", new_size: %" G_GUINT64_FORMAT ", max. size %" G_GUINT64_FORMAT,
609             multifilesink->cur_file_size, new_size,
610             multifilesink->max_file_size);
611
612         if (multifilesink->file != NULL)
613           gst_multi_file_sink_close_file (multifilesink, NULL);
614       }
615
616       if (multifilesink->file == NULL) {
617         if (!gst_multi_file_sink_open_next_file (multifilesink))
618           goto stdio_write_error;
619
620         gst_multi_file_sink_write_stream_headers (multifilesink);
621       }
622
623       ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
624           multifilesink->file);
625
626       if (ret != 1)
627         goto stdio_write_error;
628
629       multifilesink->cur_file_size += GST_BUFFER_SIZE (buffer);
630       break;
631     }
632     default:
633       g_assert_not_reached ();
634   }
635
636   return GST_FLOW_OK;
637
638   /* ERRORS */
639 write_error:
640   {
641     switch (error->code) {
642       case G_FILE_ERROR_NOSPC:{
643         GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
644             (NULL));
645         break;
646       }
647       default:{
648         GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
649             ("Error while writing to file \"%s\".", filename),
650             ("%s", g_strerror (errno)));
651       }
652     }
653     g_error_free (error);
654     g_free (filename);
655
656     return GST_FLOW_ERROR;
657   }
658 stdio_write_error:
659   switch (errno) {
660     case ENOSPC:
661       GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT,
662           ("Error while writing to file."), ("%s", g_strerror (errno)));
663       break;
664     default:
665       GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
666           ("Error while writing to file."), ("%s", g_strerror (errno)));
667   }
668   return GST_FLOW_ERROR;
669 }
670
671 static GstBufferListItem
672 buffer_list_calc_size (GstBuffer ** buf, guint group, guint idx, gpointer data)
673 {
674   guint *p_size = data;
675   guint buf_size;
676
677   buf_size = GST_BUFFER_SIZE (*buf);
678   GST_TRACE ("buffer %u in group %u has size %u", idx, group, buf_size);
679   *p_size += buf_size;
680
681   return GST_BUFFER_LIST_CONTINUE;
682 }
683
684 static GstBufferListItem
685 buffer_list_copy_data (GstBuffer ** buf, guint group, guint idx, gpointer data)
686 {
687   GstBuffer *dest = data;
688
689   if (group == 0 && idx == 0)
690     gst_buffer_copy_metadata (dest, *buf, GST_BUFFER_COPY_ALL);
691
692   memcpy (GST_BUFFER_DATA (dest) + GST_BUFFER_SIZE (dest),
693       GST_BUFFER_DATA (*buf), GST_BUFFER_SIZE (*buf));
694   GST_BUFFER_SIZE (dest) += GST_BUFFER_SIZE (*buf);
695
696   return GST_BUFFER_LIST_CONTINUE;
697 }
698
699 /* Our assumption for now is that the buffers in a buffer list should always
700  * end up in the same file. If someone wants different behaviour, they'll just
701  * have to add a property for that. */
702 static GstFlowReturn
703 gst_multi_file_sink_render_list (GstBaseSink * sink, GstBufferList * list)
704 {
705   GstBuffer *buf;
706   guint size;
707
708   gst_buffer_list_foreach (list, buffer_list_calc_size, &size);
709   GST_LOG_OBJECT (sink, "total size of buffer list %p: %u", list, size);
710
711   /* copy all buffers in the list into one single buffer, so we can use
712    * the normal render function (FIXME: optimise to avoid the memcpy) */
713   buf = gst_buffer_new_and_alloc (size);
714   GST_BUFFER_SIZE (buf) = 0;
715   gst_buffer_list_foreach (list, buffer_list_copy_data, buf);
716   g_assert (GST_BUFFER_SIZE (buf) == size);
717
718   gst_multi_file_sink_render (sink, buf);
719   gst_buffer_unref (buf);
720
721   return GST_FLOW_OK;
722 }
723
724 static gboolean
725 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
726 {
727   GstMultiFileSink *multifilesink;
728   GstStructure *structure;
729
730   multifilesink = GST_MULTI_FILE_SINK (sink);
731
732   structure = gst_caps_get_structure (caps, 0);
733   if (structure) {
734     const GValue *value;
735
736     value = gst_structure_get_value (structure, "streamheader");
737
738     if (GST_VALUE_HOLDS_ARRAY (value)) {
739       int i;
740
741       if (multifilesink->streamheaders) {
742         for (i = 0; i < multifilesink->n_streamheaders; i++) {
743           gst_buffer_unref (multifilesink->streamheaders[i]);
744         }
745         g_free (multifilesink->streamheaders);
746       }
747
748       multifilesink->n_streamheaders = gst_value_array_get_size (value);
749       multifilesink->streamheaders =
750           g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
751
752       for (i = 0; i < multifilesink->n_streamheaders; i++) {
753         multifilesink->streamheaders[i] =
754             gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value
755                 (value, i)));
756       }
757     }
758   }
759
760   return TRUE;
761 }
762
763 static void
764 gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
765 {
766   char *filename;
767
768   while (multifilesink->max_files &&
769       multifilesink->n_files >= multifilesink->max_files) {
770     filename = multifilesink->files->data;
771     g_remove (filename);
772     g_free (filename);
773     multifilesink->files = g_slist_delete_link (multifilesink->files,
774         multifilesink->files);
775     multifilesink->n_files -= 1;
776   }
777 }
778
779 static gboolean
780 gst_multi_file_sink_event (GstBaseSink * sink, GstEvent * event)
781 {
782   GstMultiFileSink *multifilesink;
783   gchar *filename;
784   gboolean res = TRUE;
785
786   multifilesink = GST_MULTI_FILE_SINK (sink);
787
788   switch (GST_EVENT_TYPE (event)) {
789     case GST_EVENT_CUSTOM_DOWNSTREAM:
790     {
791       GstClockTime timestamp, duration;
792       GstClockTime running_time, stream_time;
793       guint64 offset, offset_end;
794       gboolean all_headers;
795       guint count;
796
797       if (multifilesink->next_file != GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT ||
798           !gst_video_event_is_force_key_unit (event))
799         goto out;
800
801       gst_video_event_parse_downstream_force_key_unit (event, &timestamp,
802           &stream_time, &running_time, &all_headers, &count);
803
804       if (multifilesink->force_key_unit_count != -1 &&
805           multifilesink->force_key_unit_count == count)
806         goto out;
807
808       multifilesink->force_key_unit_count = count;
809
810       if (multifilesink->file) {
811         duration = GST_CLOCK_TIME_NONE;
812         offset = offset_end = -1;
813         filename = g_strdup_printf (multifilesink->filename,
814             multifilesink->index);
815         gst_multi_file_sink_post_message_full (multifilesink, timestamp,
816             duration, offset, offset_end, running_time, stream_time, filename);
817
818         g_free (filename);
819
820         gst_multi_file_sink_close_file (multifilesink, NULL);
821
822       }
823
824       if (multifilesink->file == NULL) {
825         if (!gst_multi_file_sink_open_next_file (multifilesink))
826           goto stdio_write_error;
827       }
828
829       break;
830     }
831     default:
832       break;
833   }
834
835 out:
836   return res;
837
838 stdio_write_error:
839   GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
840       ("Error while writing to file."), (NULL));
841   return FALSE;
842 }
843
844 static gboolean
845 gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
846 {
847   char *filename;
848
849   g_return_val_if_fail (multifilesink->file == NULL, FALSE);
850
851   gst_multi_file_sink_ensure_max_files (multifilesink);
852   filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
853   multifilesink->file = g_fopen (filename, "wb");
854   if (multifilesink->file == NULL) {
855     g_free (filename);
856     return FALSE;
857   }
858
859   GST_INFO_OBJECT (multifilesink, "opening file %s", filename);
860   multifilesink->files = g_slist_append (multifilesink->files, filename);
861   multifilesink->n_files += 1;
862
863   multifilesink->cur_file_size = 0;
864   return TRUE;
865 }
866
867 static void
868 gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
869     GstBuffer * buffer)
870 {
871   char *filename;
872
873   fclose (multifilesink->file);
874   multifilesink->file = NULL;
875
876   if (buffer) {
877     filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
878     gst_multi_file_sink_post_message (multifilesink, buffer, filename);
879     g_free (filename);
880   }
881
882   multifilesink->index++;
883 }