multifilesink: don't write stream header twice for first file
[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  *                    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 #define gst_multi_file_sink_parent_class parent_class
196 G_DEFINE_TYPE (GstMultiFileSink, gst_multi_file_sink, GST_TYPE_BASE_SINK);
197
198 static void
199 gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
200 {
201   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
202   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
203   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
204
205   gobject_class->set_property = gst_multi_file_sink_set_property;
206   gobject_class->get_property = gst_multi_file_sink_get_property;
207
208   g_object_class_install_property (gobject_class, PROP_LOCATION,
209       g_param_spec_string ("location", "File Location",
210           "Location of the file to write", NULL,
211           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212
213   g_object_class_install_property (gobject_class, PROP_INDEX,
214       g_param_spec_int ("index", "Index",
215           "Index to use with location property to create file names.  The "
216           "index is incremented by one for each buffer written.",
217           0, G_MAXINT, DEFAULT_INDEX,
218           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
219   /**
220    * GstMultiFileSink:post-messages
221    *
222    * Post a message on the GstBus for each file.
223    *
224    * Since: 0.10.17
225    */
226   g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
227       g_param_spec_boolean ("post-messages", "Post Messages",
228           "Post a message for each file with information of the buffer",
229           DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
230   /**
231    * GstMultiFileSink:next-file
232    *
233    * When to start a new file.
234    *
235    * Since: 0.10.17
236    */
237   g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
238       g_param_spec_enum ("next-file", "Next File",
239           "When to start a new file",
240           GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
241           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
242
243
244   /**
245    * GstMultiFileSink:max-files
246    *
247    * Maximum number of files to keep on disk. Once the maximum is reached, old
248    * files start to be deleted to make room for new ones.
249    *
250    * Since: 0.10.31
251    */
252   g_object_class_install_property (gobject_class, PROP_MAX_FILES,
253       g_param_spec_uint ("max-files", "Max files",
254           "Maximum number of files to keep on disk. Once the maximum is reached,"
255           "old files start to be deleted to make room for new ones.",
256           0, G_MAXUINT, DEFAULT_MAX_FILES,
257           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
258
259   /**
260    * GstMultiFileSink:max-file-size
261    *
262    * Maximum file size before starting a new file in max-size mode.
263    *
264    * Since: 0.10.31
265    */
266   g_object_class_install_property (gobject_class, PROP_MAX_FILE_SIZE,
267       g_param_spec_uint64 ("max-file-size", "Maximum File Size",
268           "Maximum file size before starting a new file in max-size mode",
269           0, G_MAXUINT64, DEFAULT_MAX_FILE_SIZE,
270           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
271
272   gobject_class->finalize = gst_multi_file_sink_finalize;
273
274   gstbasesink_class->get_times = NULL;
275   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
276   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
277   gstbasesink_class->render_list =
278       GST_DEBUG_FUNCPTR (gst_multi_file_sink_render_list);
279   gstbasesink_class->set_caps =
280       GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
281   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_multi_file_sink_event);
282
283   GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
284       "multifilesink element");
285
286   gst_element_class_add_pad_template (gstelement_class,
287       gst_static_pad_template_get (&sinktemplate));
288   gst_element_class_set_static_metadata (gstelement_class, "Multi-File Sink",
289       "Sink/File",
290       "Write buffers to a sequentially named set of files",
291       "David Schleef <ds@schleef.org>");
292 }
293
294 static void
295 gst_multi_file_sink_init (GstMultiFileSink * multifilesink)
296 {
297   multifilesink->filename = g_strdup (DEFAULT_LOCATION);
298   multifilesink->index = DEFAULT_INDEX;
299   multifilesink->post_messages = DEFAULT_POST_MESSAGES;
300   multifilesink->max_files = DEFAULT_MAX_FILES;
301   multifilesink->max_file_size = DEFAULT_MAX_FILE_SIZE;
302   multifilesink->files = NULL;
303   multifilesink->n_files = 0;
304
305   gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
306
307   multifilesink->next_segment = GST_CLOCK_TIME_NONE;
308   multifilesink->force_key_unit_count = -1;
309 }
310
311 static void
312 gst_multi_file_sink_finalize (GObject * object)
313 {
314   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
315
316   g_free (sink->filename);
317   g_slist_foreach (sink->files, (GFunc) g_free, NULL);
318   g_slist_free (sink->files);
319
320   G_OBJECT_CLASS (parent_class)->finalize (object);
321 }
322
323 static gboolean
324 gst_multi_file_sink_set_location (GstMultiFileSink * sink,
325     const gchar * location)
326 {
327   g_free (sink->filename);
328   /* FIXME: validate location to have just one %d */
329   sink->filename = g_strdup (location);
330
331   return TRUE;
332 }
333
334 static void
335 gst_multi_file_sink_set_property (GObject * object, guint prop_id,
336     const GValue * value, GParamSpec * pspec)
337 {
338   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
339
340   switch (prop_id) {
341     case PROP_LOCATION:
342       gst_multi_file_sink_set_location (sink, g_value_get_string (value));
343       break;
344     case PROP_INDEX:
345       sink->index = g_value_get_int (value);
346       break;
347     case PROP_POST_MESSAGES:
348       sink->post_messages = g_value_get_boolean (value);
349       break;
350     case PROP_NEXT_FILE:
351       sink->next_file = g_value_get_enum (value);
352       break;
353     case PROP_MAX_FILES:
354       sink->max_files = g_value_get_uint (value);
355       break;
356     case PROP_MAX_FILE_SIZE:
357       sink->max_file_size = g_value_get_uint64 (value);
358       break;
359     default:
360       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
361       break;
362   }
363 }
364
365 static void
366 gst_multi_file_sink_get_property (GObject * object, guint prop_id,
367     GValue * value, GParamSpec * pspec)
368 {
369   GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
370
371   switch (prop_id) {
372     case PROP_LOCATION:
373       g_value_set_string (value, sink->filename);
374       break;
375     case PROP_INDEX:
376       g_value_set_int (value, sink->index);
377       break;
378     case PROP_POST_MESSAGES:
379       g_value_set_boolean (value, sink->post_messages);
380       break;
381     case PROP_NEXT_FILE:
382       g_value_set_enum (value, sink->next_file);
383       break;
384     case PROP_MAX_FILES:
385       g_value_set_uint (value, sink->max_files);
386       break;
387     case PROP_MAX_FILE_SIZE:
388       g_value_set_uint64 (value, sink->max_file_size);
389       break;
390     default:
391       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
392       break;
393   }
394 }
395
396 static gboolean
397 gst_multi_file_sink_stop (GstBaseSink * sink)
398 {
399   GstMultiFileSink *multifilesink;
400   int i;
401
402   multifilesink = GST_MULTI_FILE_SINK (sink);
403
404   if (multifilesink->file != NULL) {
405     fclose (multifilesink->file);
406     multifilesink->file = NULL;
407   }
408
409   if (multifilesink->streamheaders) {
410     for (i = 0; i < multifilesink->n_streamheaders; i++) {
411       gst_buffer_unref (multifilesink->streamheaders[i]);
412     }
413     g_free (multifilesink->streamheaders);
414     multifilesink->streamheaders = NULL;
415   }
416
417   multifilesink->force_key_unit_count = -1;
418
419   return TRUE;
420 }
421
422
423 static void
424 gst_multi_file_sink_post_message_full (GstMultiFileSink * multifilesink,
425     GstClockTime timestamp, GstClockTime duration, GstClockTime offset,
426     GstClockTime offset_end, GstClockTime running_time,
427     GstClockTime stream_time, const char *filename)
428 {
429   GstStructure *s;
430
431   if (!multifilesink->post_messages)
432     return;
433
434   s = gst_structure_new ("GstMultiFileSink",
435       "filename", G_TYPE_STRING, filename,
436       "index", G_TYPE_INT, multifilesink->index,
437       "timestamp", G_TYPE_UINT64, timestamp,
438       "stream-time", G_TYPE_UINT64, stream_time,
439       "running-time", G_TYPE_UINT64, running_time,
440       "duration", G_TYPE_UINT64, duration,
441       "offset", G_TYPE_UINT64, offset,
442       "offset-end", G_TYPE_UINT64, offset_end, NULL);
443
444   gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
445       gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
446 }
447
448
449 static void
450 gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
451     GstBuffer * buffer, const char *filename)
452 {
453   GstClockTime duration, timestamp;
454   GstClockTime running_time, stream_time;
455   guint64 offset, offset_end;
456   GstSegment *segment;
457   GstFormat format;
458
459   if (!multifilesink->post_messages)
460     return;
461
462   segment = &GST_BASE_SINK (multifilesink)->segment;
463   format = segment->format;
464
465   timestamp = GST_BUFFER_TIMESTAMP (buffer);
466   duration = GST_BUFFER_DURATION (buffer);
467   offset = GST_BUFFER_OFFSET (buffer);
468   offset_end = GST_BUFFER_OFFSET_END (buffer);
469
470   running_time = gst_segment_to_running_time (segment, format, timestamp);
471   stream_time = gst_segment_to_stream_time (segment, format, timestamp);
472
473   gst_multi_file_sink_post_message_full (multifilesink, timestamp, duration,
474       offset, offset_end, running_time, stream_time, filename);
475 }
476
477 static gboolean
478 gst_multi_file_sink_write_stream_headers (GstMultiFileSink * sink)
479 {
480   int i;
481
482   if (sink->streamheaders == NULL)
483     return TRUE;
484
485   /* we want to write these at the beginning */
486   g_assert (sink->cur_file_size == 0);
487
488   for (i = 0; i < sink->n_streamheaders; i++) {
489     GstBuffer *hdr;
490     GstMapInfo map;
491     int ret;
492
493     hdr = sink->streamheaders[i];
494     gst_buffer_map (hdr, &map, GST_MAP_READ);
495     ret = fwrite (map.data, map.size, 1, sink->file);
496     gst_buffer_unmap (hdr, &map);
497
498     if (ret != 1)
499       return FALSE;
500
501     sink->cur_file_size += map.size;
502   }
503
504   return TRUE;
505 }
506
507 static GstFlowReturn
508 gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
509 {
510   GstMultiFileSink *multifilesink;
511   GstMapInfo map;
512   gchar *filename;
513   gboolean ret;
514   GError *error = NULL;
515   gboolean first_file = TRUE;
516
517   gst_buffer_map (buffer, &map, GST_MAP_READ);
518
519   multifilesink = GST_MULTI_FILE_SINK (sink);
520
521   switch (multifilesink->next_file) {
522     case GST_MULTI_FILE_SINK_NEXT_BUFFER:
523       gst_multi_file_sink_ensure_max_files (multifilesink);
524
525       filename = g_strdup_printf (multifilesink->filename,
526           multifilesink->index);
527       ret = g_file_set_contents (filename, (char *) map.data, map.size, &error);
528       if (!ret)
529         goto write_error;
530
531       multifilesink->files = g_slist_append (multifilesink->files, filename);
532       multifilesink->n_files += 1;
533
534       gst_multi_file_sink_post_message (multifilesink, buffer, filename);
535       multifilesink->index++;
536
537       break;
538     case GST_MULTI_FILE_SINK_NEXT_DISCONT:
539       if (GST_BUFFER_IS_DISCONT (buffer)) {
540         if (multifilesink->file)
541           gst_multi_file_sink_close_file (multifilesink, buffer);
542       }
543
544       if (multifilesink->file == NULL) {
545         if (!gst_multi_file_sink_open_next_file (multifilesink))
546           goto stdio_write_error;
547       }
548
549       ret = fwrite (map.data, map.size, 1, multifilesink->file);
550       if (ret != 1)
551         goto stdio_write_error;
552
553       break;
554     case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
555       if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
556         if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
557           multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
558               10 * GST_SECOND;
559         }
560       }
561
562       if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
563           GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
564           !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
565         if (multifilesink->file) {
566           first_file = FALSE;
567           gst_multi_file_sink_close_file (multifilesink, buffer);
568         }
569         multifilesink->next_segment += 10 * GST_SECOND;
570       }
571
572       if (multifilesink->file == NULL) {
573         if (!gst_multi_file_sink_open_next_file (multifilesink))
574           goto stdio_write_error;
575
576         if (!first_file)
577           gst_multi_file_sink_write_stream_headers (multifilesink);
578       }
579
580       ret = fwrite (map.data, map.size, 1, multifilesink->file);
581       if (ret != 1)
582         goto stdio_write_error;
583
584       break;
585     case GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT:
586       if (multifilesink->file == NULL) {
587         if (!gst_multi_file_sink_open_next_file (multifilesink))
588           goto stdio_write_error;
589       }
590
591       if (!gst_multi_file_sink_write_stream_headers (multifilesink))
592         goto stdio_write_error;
593
594       ret = fwrite (map.data, map.size, 1, multifilesink->file);
595
596       if (ret != 1)
597         goto stdio_write_error;
598
599       break;
600     case GST_MULTI_FILE_SINK_NEXT_MAX_SIZE:{
601       guint64 new_size;
602
603       new_size = multifilesink->cur_file_size + map.size;
604       if (new_size > multifilesink->max_file_size) {
605
606         GST_INFO_OBJECT (multifilesink, "current size: %" G_GUINT64_FORMAT
607             ", new_size: %" G_GUINT64_FORMAT ", max. size %" G_GUINT64_FORMAT,
608             multifilesink->cur_file_size, new_size,
609             multifilesink->max_file_size);
610
611         if (multifilesink->file != NULL) {
612           first_file = FALSE;
613           gst_multi_file_sink_close_file (multifilesink, NULL);
614         }
615       }
616
617       if (multifilesink->file == NULL) {
618         if (!gst_multi_file_sink_open_next_file (multifilesink))
619           goto stdio_write_error;
620
621         if (!first_file)
622           gst_multi_file_sink_write_stream_headers (multifilesink);
623       }
624
625       ret = fwrite (map.data, map.size, 1, multifilesink->file);
626
627       if (ret != 1)
628         goto stdio_write_error;
629
630       multifilesink->cur_file_size += map.size;
631       break;
632     }
633     default:
634       g_assert_not_reached ();
635   }
636
637   gst_buffer_unmap (buffer, &map);
638   return GST_FLOW_OK;
639
640   /* ERRORS */
641 write_error:
642   {
643     switch (error->code) {
644       case G_FILE_ERROR_NOSPC:{
645         GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
646             (NULL));
647         break;
648       }
649       default:{
650         GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
651             ("Error while writing to file \"%s\".", filename),
652             ("%s", g_strerror (errno)));
653       }
654     }
655     g_error_free (error);
656     g_free (filename);
657
658     gst_buffer_unmap (buffer, &map);
659     return GST_FLOW_ERROR;
660   }
661 stdio_write_error:
662   switch (errno) {
663     case ENOSPC:
664       GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT,
665           ("Error while writing to file."), ("%s", g_strerror (errno)));
666       break;
667     default:
668       GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
669           ("Error while writing to file."), ("%s", g_strerror (errno)));
670   }
671   gst_buffer_unmap (buffer, &map);
672   return GST_FLOW_ERROR;
673 }
674
675 static gboolean
676 buffer_list_calc_size (GstBuffer ** buf, guint idx, gpointer data)
677 {
678   guint *p_size = data;
679   gsize buf_size;
680
681   buf_size = gst_buffer_get_size (*buf);
682   GST_TRACE ("buffer %u has size %" G_GSIZE_FORMAT, idx, buf_size);
683   *p_size += buf_size;
684
685   return TRUE;
686 }
687
688 static gboolean
689 buffer_list_copy_data (GstBuffer ** buf, guint idx, gpointer data)
690 {
691   GstBuffer *dest = data;
692   guint num, i;
693
694   if (idx == 0)
695     gst_buffer_copy_into (dest, *buf, GST_BUFFER_COPY_METADATA, 0, -1);
696
697   num = gst_buffer_n_memory (*buf);
698   for (i = 0; i < num; ++i) {
699     GstMemory *mem;
700
701     mem = gst_buffer_get_memory (*buf, i);
702     gst_buffer_append_memory (dest, mem);
703   }
704
705   return TRUE;
706 }
707
708 /* Our assumption for now is that the buffers in a buffer list should always
709  * end up in the same file. If someone wants different behaviour, they'll just
710  * have to add a property for that. */
711 static GstFlowReturn
712 gst_multi_file_sink_render_list (GstBaseSink * sink, GstBufferList * list)
713 {
714   GstBuffer *buf;
715   guint size = 0;
716
717   gst_buffer_list_foreach (list, buffer_list_calc_size, &size);
718   GST_LOG_OBJECT (sink, "total size of buffer list %p: %u", list, size);
719
720   /* copy all buffers in the list into one single buffer, so we can use
721    * the normal render function (FIXME: optimise to avoid the memcpy) */
722   buf = gst_buffer_new ();
723   gst_buffer_list_foreach (list, buffer_list_copy_data, buf);
724   g_assert (gst_buffer_get_size (buf) == size);
725
726   gst_multi_file_sink_render (sink, buf);
727   gst_buffer_unref (buf);
728
729   return GST_FLOW_OK;
730 }
731
732 static gboolean
733 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
734 {
735   GstMultiFileSink *multifilesink;
736   GstStructure *structure;
737
738   multifilesink = GST_MULTI_FILE_SINK (sink);
739
740   structure = gst_caps_get_structure (caps, 0);
741   if (structure) {
742     const GValue *value;
743
744     value = gst_structure_get_value (structure, "streamheader");
745
746     if (GST_VALUE_HOLDS_ARRAY (value)) {
747       int i;
748
749       if (multifilesink->streamheaders) {
750         for (i = 0; i < multifilesink->n_streamheaders; i++) {
751           gst_buffer_unref (multifilesink->streamheaders[i]);
752         }
753         g_free (multifilesink->streamheaders);
754       }
755
756       multifilesink->n_streamheaders = gst_value_array_get_size (value);
757       multifilesink->streamheaders =
758           g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
759
760       for (i = 0; i < multifilesink->n_streamheaders; i++) {
761         multifilesink->streamheaders[i] =
762             gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value
763                 (value, i)));
764       }
765     }
766   }
767
768   return TRUE;
769 }
770
771 static void
772 gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
773 {
774   char *filename;
775
776   while (multifilesink->max_files &&
777       multifilesink->n_files >= multifilesink->max_files) {
778     filename = multifilesink->files->data;
779     g_remove (filename);
780     g_free (filename);
781     multifilesink->files = g_slist_delete_link (multifilesink->files,
782         multifilesink->files);
783     multifilesink->n_files -= 1;
784   }
785 }
786
787 static gboolean
788 gst_multi_file_sink_event (GstBaseSink * sink, GstEvent * event)
789 {
790   GstMultiFileSink *multifilesink;
791   gchar *filename;
792
793   multifilesink = GST_MULTI_FILE_SINK (sink);
794
795   switch (GST_EVENT_TYPE (event)) {
796     case GST_EVENT_CUSTOM_DOWNSTREAM:
797     {
798       GstClockTime timestamp, duration;
799       GstClockTime running_time, stream_time;
800       guint64 offset, offset_end;
801       gboolean all_headers;
802       guint count;
803
804       if (multifilesink->next_file != GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT ||
805           !gst_video_event_is_force_key_unit (event))
806         goto out;
807
808       gst_video_event_parse_downstream_force_key_unit (event, &timestamp,
809           &stream_time, &running_time, &all_headers, &count);
810
811       if (multifilesink->force_key_unit_count != -1 &&
812           multifilesink->force_key_unit_count == count)
813         goto out;
814
815       multifilesink->force_key_unit_count = count;
816
817       if (multifilesink->file) {
818         duration = GST_CLOCK_TIME_NONE;
819         offset = offset_end = -1;
820         filename = g_strdup_printf (multifilesink->filename,
821             multifilesink->index);
822         gst_multi_file_sink_post_message_full (multifilesink, timestamp,
823             duration, offset, offset_end, running_time, stream_time, filename);
824
825         g_free (filename);
826
827         gst_multi_file_sink_close_file (multifilesink, NULL);
828
829       }
830
831       if (multifilesink->file == NULL) {
832         if (!gst_multi_file_sink_open_next_file (multifilesink))
833           goto stdio_write_error;
834       }
835
836       break;
837     }
838     default:
839       break;
840   }
841
842 out:
843   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
844
845   /* ERRORS */
846 stdio_write_error:
847   {
848     GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
849         ("Error while writing to file."), (NULL));
850     gst_event_unref (event);
851     return FALSE;
852   }
853 }
854
855 static gboolean
856 gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
857 {
858   char *filename;
859
860   g_return_val_if_fail (multifilesink->file == NULL, FALSE);
861
862   gst_multi_file_sink_ensure_max_files (multifilesink);
863   filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
864   multifilesink->file = g_fopen (filename, "wb");
865   if (multifilesink->file == NULL) {
866     g_free (filename);
867     return FALSE;
868   }
869
870   GST_INFO_OBJECT (multifilesink, "opening file %s", filename);
871   multifilesink->files = g_slist_append (multifilesink->files, filename);
872   multifilesink->n_files += 1;
873
874   multifilesink->cur_file_size = 0;
875   return TRUE;
876 }
877
878 static void
879 gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
880     GstBuffer * buffer)
881 {
882   char *filename;
883
884   fclose (multifilesink->file);
885   multifilesink->file = NULL;
886
887   if (buffer) {
888     filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
889     gst_multi_file_sink_post_message (multifilesink, buffer, filename);
890     g_free (filename);
891   }
892
893   multifilesink->index++;
894 }