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