9142968547d07be14a28b45c70ba9a1d8f8fa1ce
[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
516   gst_buffer_map (buffer, &map, GST_MAP_READ);
517
518   multifilesink = GST_MULTI_FILE_SINK (sink);
519
520   switch (multifilesink->next_file) {
521     case GST_MULTI_FILE_SINK_NEXT_BUFFER:
522       gst_multi_file_sink_ensure_max_files (multifilesink);
523
524       filename = g_strdup_printf (multifilesink->filename,
525           multifilesink->index);
526       ret = g_file_set_contents (filename, (char *) map.data, map.size, &error);
527       if (!ret)
528         goto write_error;
529
530       multifilesink->files = g_slist_append (multifilesink->files, filename);
531       multifilesink->n_files += 1;
532
533       gst_multi_file_sink_post_message (multifilesink, buffer, filename);
534       multifilesink->index++;
535
536       break;
537     case GST_MULTI_FILE_SINK_NEXT_DISCONT:
538       if (GST_BUFFER_IS_DISCONT (buffer)) {
539         if (multifilesink->file)
540           gst_multi_file_sink_close_file (multifilesink, buffer);
541       }
542
543       if (multifilesink->file == NULL) {
544         if (!gst_multi_file_sink_open_next_file (multifilesink))
545           goto stdio_write_error;
546       }
547
548       ret = fwrite (map.data, map.size, 1, multifilesink->file);
549       if (ret != 1)
550         goto stdio_write_error;
551
552       break;
553     case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
554       if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
555         if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
556           multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
557               10 * GST_SECOND;
558         }
559       }
560
561       if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
562           GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
563           !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
564         if (multifilesink->file)
565           gst_multi_file_sink_close_file (multifilesink, buffer);
566
567         multifilesink->next_segment += 10 * GST_SECOND;
568       }
569
570       if (multifilesink->file == NULL) {
571         if (!gst_multi_file_sink_open_next_file (multifilesink))
572           goto stdio_write_error;
573
574         gst_multi_file_sink_write_stream_headers (multifilesink);
575       }
576
577       ret = fwrite (map.data, map.size, 1, multifilesink->file);
578       if (ret != 1)
579         goto stdio_write_error;
580
581       break;
582     case GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT:
583       if (multifilesink->file == NULL) {
584         if (!gst_multi_file_sink_open_next_file (multifilesink))
585           goto stdio_write_error;
586       }
587
588       if (!gst_multi_file_sink_write_stream_headers (multifilesink))
589         goto stdio_write_error;
590
591       ret = fwrite (map.data, map.size, 1, multifilesink->file);
592
593       if (ret != 1)
594         goto stdio_write_error;
595
596       break;
597     case GST_MULTI_FILE_SINK_NEXT_MAX_SIZE:{
598       guint64 new_size;
599
600       new_size = multifilesink->cur_file_size + map.size;
601       if (new_size > multifilesink->max_file_size) {
602
603         GST_INFO_OBJECT (multifilesink, "current size: %" G_GUINT64_FORMAT
604             ", new_size: %" G_GUINT64_FORMAT ", max. size %" G_GUINT64_FORMAT,
605             multifilesink->cur_file_size, new_size,
606             multifilesink->max_file_size);
607
608         if (multifilesink->file != NULL)
609           gst_multi_file_sink_close_file (multifilesink, NULL);
610       }
611
612       if (multifilesink->file == NULL) {
613         if (!gst_multi_file_sink_open_next_file (multifilesink))
614           goto stdio_write_error;
615
616         gst_multi_file_sink_write_stream_headers (multifilesink);
617       }
618
619       ret = fwrite (map.data, map.size, 1, multifilesink->file);
620
621       if (ret != 1)
622         goto stdio_write_error;
623
624       multifilesink->cur_file_size += map.size;
625       break;
626     }
627     default:
628       g_assert_not_reached ();
629   }
630
631   gst_buffer_unmap (buffer, &map);
632   return GST_FLOW_OK;
633
634   /* ERRORS */
635 write_error:
636   {
637     switch (error->code) {
638       case G_FILE_ERROR_NOSPC:{
639         GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
640             (NULL));
641         break;
642       }
643       default:{
644         GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
645             ("Error while writing to file \"%s\".", filename),
646             ("%s", g_strerror (errno)));
647       }
648     }
649     g_error_free (error);
650     g_free (filename);
651
652     gst_buffer_unmap (buffer, &map);
653     return GST_FLOW_ERROR;
654   }
655 stdio_write_error:
656   switch (errno) {
657     case ENOSPC:
658       GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT,
659           ("Error while writing to file."), ("%s", g_strerror (errno)));
660       break;
661     default:
662       GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
663           ("Error while writing to file."), ("%s", g_strerror (errno)));
664   }
665   gst_buffer_unmap (buffer, &map);
666   return GST_FLOW_ERROR;
667 }
668
669 static gboolean
670 buffer_list_calc_size (GstBuffer ** buf, guint idx, gpointer data)
671 {
672   guint *p_size = data;
673   gsize buf_size;
674
675   buf_size = gst_buffer_get_size (*buf);
676   GST_TRACE ("buffer %u has size %" G_GSIZE_FORMAT, idx, buf_size);
677   *p_size += buf_size;
678
679   return TRUE;
680 }
681
682 static gboolean
683 buffer_list_copy_data (GstBuffer ** buf, guint idx, gpointer data)
684 {
685   GstBuffer *dest = data;
686   guint num, i;
687
688   if (idx == 0)
689     gst_buffer_copy_into (dest, *buf, GST_BUFFER_COPY_METADATA, 0, -1);
690
691   num = gst_buffer_n_memory (*buf);
692   for (i = 0; i < num; ++i) {
693     GstMemory *mem;
694
695     mem = gst_buffer_get_memory (*buf, i);
696     gst_buffer_append_memory (dest, mem);
697   }
698
699   return TRUE;
700 }
701
702 /* Our assumption for now is that the buffers in a buffer list should always
703  * end up in the same file. If someone wants different behaviour, they'll just
704  * have to add a property for that. */
705 static GstFlowReturn
706 gst_multi_file_sink_render_list (GstBaseSink * sink, GstBufferList * list)
707 {
708   GstBuffer *buf;
709   guint size = 0;
710
711   gst_buffer_list_foreach (list, buffer_list_calc_size, &size);
712   GST_LOG_OBJECT (sink, "total size of buffer list %p: %u", list, size);
713
714   /* copy all buffers in the list into one single buffer, so we can use
715    * the normal render function (FIXME: optimise to avoid the memcpy) */
716   buf = gst_buffer_new ();
717   gst_buffer_list_foreach (list, buffer_list_copy_data, buf);
718   g_assert (gst_buffer_get_size (buf) == size);
719
720   gst_multi_file_sink_render (sink, buf);
721   gst_buffer_unref (buf);
722
723   return GST_FLOW_OK;
724 }
725
726 static gboolean
727 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
728 {
729   GstMultiFileSink *multifilesink;
730   GstStructure *structure;
731
732   multifilesink = GST_MULTI_FILE_SINK (sink);
733
734   structure = gst_caps_get_structure (caps, 0);
735   if (structure) {
736     const GValue *value;
737
738     value = gst_structure_get_value (structure, "streamheader");
739
740     if (GST_VALUE_HOLDS_ARRAY (value)) {
741       int i;
742
743       if (multifilesink->streamheaders) {
744         for (i = 0; i < multifilesink->n_streamheaders; i++) {
745           gst_buffer_unref (multifilesink->streamheaders[i]);
746         }
747         g_free (multifilesink->streamheaders);
748       }
749
750       multifilesink->n_streamheaders = gst_value_array_get_size (value);
751       multifilesink->streamheaders =
752           g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
753
754       for (i = 0; i < multifilesink->n_streamheaders; i++) {
755         multifilesink->streamheaders[i] =
756             gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value
757                 (value, i)));
758       }
759     }
760   }
761
762   return TRUE;
763 }
764
765 static void
766 gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
767 {
768   char *filename;
769
770   while (multifilesink->max_files &&
771       multifilesink->n_files >= multifilesink->max_files) {
772     filename = multifilesink->files->data;
773     g_remove (filename);
774     g_free (filename);
775     multifilesink->files = g_slist_delete_link (multifilesink->files,
776         multifilesink->files);
777     multifilesink->n_files -= 1;
778   }
779 }
780
781 static gboolean
782 gst_multi_file_sink_event (GstBaseSink * sink, GstEvent * event)
783 {
784   GstMultiFileSink *multifilesink;
785   gchar *filename;
786
787   multifilesink = GST_MULTI_FILE_SINK (sink);
788
789   switch (GST_EVENT_TYPE (event)) {
790     case GST_EVENT_CUSTOM_DOWNSTREAM:
791     {
792       GstClockTime timestamp, duration;
793       GstClockTime running_time, stream_time;
794       guint64 offset, offset_end;
795       gboolean all_headers;
796       guint count;
797
798       if (multifilesink->next_file != GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT ||
799           !gst_video_event_is_force_key_unit (event))
800         goto out;
801
802       gst_video_event_parse_downstream_force_key_unit (event, &timestamp,
803           &stream_time, &running_time, &all_headers, &count);
804
805       if (multifilesink->force_key_unit_count != -1 &&
806           multifilesink->force_key_unit_count == count)
807         goto out;
808
809       multifilesink->force_key_unit_count = count;
810
811       if (multifilesink->file) {
812         duration = GST_CLOCK_TIME_NONE;
813         offset = offset_end = -1;
814         filename = g_strdup_printf (multifilesink->filename,
815             multifilesink->index);
816         gst_multi_file_sink_post_message_full (multifilesink, timestamp,
817             duration, offset, offset_end, running_time, stream_time, filename);
818
819         g_free (filename);
820
821         gst_multi_file_sink_close_file (multifilesink, NULL);
822
823       }
824
825       if (multifilesink->file == NULL) {
826         if (!gst_multi_file_sink_open_next_file (multifilesink))
827           goto stdio_write_error;
828       }
829
830       break;
831     }
832     default:
833       break;
834   }
835
836 out:
837   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
838
839   /* ERRORS */
840 stdio_write_error:
841   {
842     GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
843         ("Error while writing to file."), (NULL));
844     gst_event_unref (event);
845     return FALSE;
846   }
847 }
848
849 static gboolean
850 gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
851 {
852   char *filename;
853
854   g_return_val_if_fail (multifilesink->file == NULL, FALSE);
855
856   gst_multi_file_sink_ensure_max_files (multifilesink);
857   filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
858   multifilesink->file = g_fopen (filename, "wb");
859   if (multifilesink->file == NULL) {
860     g_free (filename);
861     return FALSE;
862   }
863
864   GST_INFO_OBJECT (multifilesink, "opening file %s", filename);
865   multifilesink->files = g_slist_append (multifilesink->files, filename);
866   multifilesink->n_files += 1;
867
868   multifilesink->cur_file_size = 0;
869   return TRUE;
870 }
871
872 static void
873 gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
874     GstBuffer * buffer)
875 {
876   char *filename;
877
878   fclose (multifilesink->file);
879   multifilesink->file = NULL;
880
881   if (buffer) {
882     filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
883     gst_multi_file_sink_post_message (multifilesink, buffer, filename);
884     g_free (filename);
885   }
886
887   multifilesink->index++;
888 }