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>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 * SECTION:element-multifilesink
26 * @see_also: #GstFileSrc
28 * Write incoming data to a series of sequentially-named files.
30 * The filename property should contain a string with a \%d placeholder that will
31 * be substituted with the index for each filename.
33 * If the #GstMultiFileSink:post-messages property is #TRUE, it sends an application
35 * <classname>"GstMultiFileSink"</classname> after writing each
38 * The message's structure contains these fields:
43 * <classname>"filename"</classname>:
44 * the filename where the buffer was written.
50 * <classname>"index"</classname>:
51 * the index of the buffer.
57 * <classname>"timestamp"</classname>:
58 * the timestamp of the buffer.
64 * <classname>"stream-time"</classname>:
65 * the stream time of the buffer.
71 * <classname>"running-time"</classname>:
72 * the running_time of the buffer.
78 * <classname>"duration"</classname>:
79 * the duration of the buffer.
85 * <classname>"offset"</classname>:
86 * the offset of the buffer that triggered the message.
92 * <classname>"offset-end"</classname>:
93 * the offset-end of the buffer that triggered the message.
99 * <title>Example launch line</title>
101 * gst-launch audiotestsrc ! multifilesink
102 * gst-launch videotestsrc ! multifilesink post-messages=true filename="frame%d"
106 * Last reviewed on 2009-09-11 (0.10.17)
112 #include <gst/base/gstbasetransform.h>
113 #include <glib/gstdio.h>
114 #include "gstmultifilesink.h"
116 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
119 GST_STATIC_CAPS_ANY);
121 GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug);
122 #define GST_CAT_DEFAULT gst_multi_file_sink_debug
124 #define DEFAULT_LOCATION "%05d"
125 #define DEFAULT_INDEX 0
126 #define DEFAULT_POST_MESSAGES FALSE
127 #define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER
128 #define DEFAULT_MAX_FILES 0
141 static void gst_multi_file_sink_finalize (GObject * object);
143 static void gst_multi_file_sink_set_property (GObject * object, guint prop_id,
144 const GValue * value, GParamSpec * pspec);
145 static void gst_multi_file_sink_get_property (GObject * object, guint prop_id,
146 GValue * value, GParamSpec * pspec);
148 static gboolean gst_multi_file_sink_stop (GstBaseSink * sink);
149 static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
151 static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink,
153 static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink *
155 static void gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
157 static void gst_multi_file_sink_ensure_max_files (GstMultiFileSink *
160 #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
162 gst_multi_file_sink_next_get_type (void)
164 static GType multi_file_sync_next_type = 0;
165 static const GEnumValue next_types[] = {
166 {GST_MULTI_FILE_SINK_NEXT_BUFFER, "New file for each buffer", "buffer"},
167 {GST_MULTI_FILE_SINK_NEXT_DISCONT, "New file after each discontinuity",
169 {GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, "New file at each key frame "
170 "(Useful for MPEG-TS segmenting)", "key-frame"},
174 if (!multi_file_sync_next_type) {
175 multi_file_sync_next_type =
176 g_enum_register_static ("GstMultiFileSinkNext", next_types);
179 return multi_file_sync_next_type;
182 GST_BOILERPLATE (GstMultiFileSink, gst_multi_file_sink, GstBaseSink,
186 gst_multi_file_sink_base_init (gpointer g_class)
188 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
190 GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
191 "multifilesink element");
193 gst_element_class_add_static_pad_template (gstelement_class,
195 gst_element_class_set_details_simple (gstelement_class, "Multi-File Sink",
197 "Write buffers to a sequentially named set of files",
198 "David Schleef <ds@schleef.org>");
202 gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
204 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
205 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
207 gobject_class->set_property = gst_multi_file_sink_set_property;
208 gobject_class->get_property = gst_multi_file_sink_get_property;
210 g_object_class_install_property (gobject_class, PROP_LOCATION,
211 g_param_spec_string ("location", "File Location",
212 "Location of the file to write", NULL,
213 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
215 g_object_class_install_property (gobject_class, PROP_INDEX,
216 g_param_spec_int ("index", "Index",
217 "Index to use with location property to create file names. The "
218 "index is incremented by one for each buffer written.",
219 0, G_MAXINT, DEFAULT_INDEX,
220 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
222 * GstMultiFileSink:post-messages
224 * Post a message on the GstBus for each file.
228 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
229 g_param_spec_boolean ("post-messages", "Post Messages",
230 "Post a message for each file with information of the buffer",
231 DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
233 * GstMultiFileSink:next-file
235 * When to start a new file.
239 g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
240 g_param_spec_enum ("next-file", "Next File",
241 "When to start a new file",
242 GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
243 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
247 * GstMultiFileSink:max-files
249 * Maximum number of files to keep on disk. Once the maximum is reached, old
250 * files start to be deleted to make room for new ones.
254 g_object_class_install_property (gobject_class, PROP_MAX_FILES,
255 g_param_spec_uint ("max-files", "Max files",
256 "Maximum number of files to keep on disk. Once the maximum is reached,"
257 "old files start to be deleted to make room for new ones.",
258 0, G_MAXUINT, DEFAULT_MAX_FILES,
259 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
261 gobject_class->finalize = gst_multi_file_sink_finalize;
263 gstbasesink_class->get_times = NULL;
264 gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
265 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
266 gstbasesink_class->set_caps =
267 GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
271 gst_multi_file_sink_init (GstMultiFileSink * multifilesink,
272 GstMultiFileSinkClass * g_class)
274 multifilesink->filename = g_strdup (DEFAULT_LOCATION);
275 multifilesink->index = DEFAULT_INDEX;
276 multifilesink->post_messages = DEFAULT_POST_MESSAGES;
277 multifilesink->max_files = DEFAULT_MAX_FILES;
278 multifilesink->files = NULL;
279 multifilesink->n_files = 0;
281 gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
283 multifilesink->next_segment = GST_CLOCK_TIME_NONE;
287 gst_multi_file_sink_finalize (GObject * object)
289 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
291 g_free (sink->filename);
292 g_slist_foreach (sink->files, (GFunc) g_free, NULL);
293 g_slist_free (sink->files);
295 G_OBJECT_CLASS (parent_class)->finalize (object);
299 gst_multi_file_sink_set_location (GstMultiFileSink * sink,
300 const gchar * location)
302 g_free (sink->filename);
303 /* FIXME: validate location to have just one %d */
304 sink->filename = g_strdup (location);
310 gst_multi_file_sink_set_property (GObject * object, guint prop_id,
311 const GValue * value, GParamSpec * pspec)
313 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
317 gst_multi_file_sink_set_location (sink, g_value_get_string (value));
320 sink->index = g_value_get_int (value);
322 case PROP_POST_MESSAGES:
323 sink->post_messages = g_value_get_boolean (value);
326 sink->next_file = g_value_get_enum (value);
329 sink->max_files = g_value_get_uint (value);
332 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
338 gst_multi_file_sink_get_property (GObject * object, guint prop_id,
339 GValue * value, GParamSpec * pspec)
341 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
345 g_value_set_string (value, sink->filename);
348 g_value_set_int (value, sink->index);
350 case PROP_POST_MESSAGES:
351 g_value_set_boolean (value, sink->post_messages);
354 g_value_set_enum (value, sink->next_file);
357 g_value_set_uint (value, sink->max_files);
360 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
366 gst_multi_file_sink_stop (GstBaseSink * sink)
368 GstMultiFileSink *multifilesink;
371 multifilesink = GST_MULTI_FILE_SINK (sink);
373 if (multifilesink->file != NULL) {
374 fclose (multifilesink->file);
375 multifilesink->file = NULL;
378 if (multifilesink->streamheaders) {
379 for (i = 0; i < multifilesink->n_streamheaders; i++) {
380 gst_buffer_unref (multifilesink->streamheaders[i]);
382 g_free (multifilesink->streamheaders);
383 multifilesink->streamheaders = NULL;
391 gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
392 GstBuffer * buffer, const char *filename)
394 if (multifilesink->post_messages) {
395 GstClockTime duration, timestamp;
396 GstClockTime running_time, stream_time;
397 guint64 offset, offset_end;
402 segment = &GST_BASE_SINK (multifilesink)->segment;
403 format = segment->format;
405 timestamp = GST_BUFFER_TIMESTAMP (buffer);
406 duration = GST_BUFFER_DURATION (buffer);
407 offset = GST_BUFFER_OFFSET (buffer);
408 offset_end = GST_BUFFER_OFFSET_END (buffer);
410 running_time = gst_segment_to_running_time (segment, format, timestamp);
411 stream_time = gst_segment_to_stream_time (segment, format, timestamp);
413 s = gst_structure_new ("GstMultiFileSink",
414 "filename", G_TYPE_STRING, filename,
415 "index", G_TYPE_INT, multifilesink->index,
416 "timestamp", G_TYPE_UINT64, timestamp,
417 "stream-time", G_TYPE_UINT64, stream_time,
418 "running-time", G_TYPE_UINT64, running_time,
419 "duration", G_TYPE_UINT64, duration,
420 "offset", G_TYPE_UINT64, offset,
421 "offset-end", G_TYPE_UINT64, offset_end, NULL);
423 gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
424 gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
429 gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
431 GstMultiFileSink *multifilesink;
436 GError *error = NULL;
438 size = GST_BUFFER_SIZE (buffer);
439 data = GST_BUFFER_DATA (buffer);
441 multifilesink = GST_MULTI_FILE_SINK (sink);
443 switch (multifilesink->next_file) {
444 case GST_MULTI_FILE_SINK_NEXT_BUFFER:
445 gst_multi_file_sink_ensure_max_files (multifilesink);
447 filename = g_strdup_printf (multifilesink->filename,
448 multifilesink->index);
449 ret = g_file_set_contents (filename, (char *) data, size, &error);
453 multifilesink->files = g_slist_append (multifilesink->files, filename);
454 multifilesink->n_files += 1;
456 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
457 multifilesink->index++;
460 case GST_MULTI_FILE_SINK_NEXT_DISCONT:
461 if (GST_BUFFER_IS_DISCONT (buffer)) {
462 if (multifilesink->file)
463 gst_multi_file_sink_close_file (multifilesink, buffer);
466 if (multifilesink->file == NULL) {
467 if (!gst_multi_file_sink_open_next_file (multifilesink))
468 goto stdio_write_error;
471 ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
472 multifilesink->file);
474 goto stdio_write_error;
477 case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
478 if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
479 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
480 multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
485 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
486 GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
487 !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
488 if (multifilesink->file)
489 gst_multi_file_sink_close_file (multifilesink, buffer);
491 multifilesink->next_segment += 10 * GST_SECOND;
494 if (multifilesink->file == NULL) {
497 if (!gst_multi_file_sink_open_next_file (multifilesink))
498 goto stdio_write_error;
500 if (multifilesink->streamheaders) {
501 for (i = 0; i < multifilesink->n_streamheaders; i++) {
502 ret = fwrite (GST_BUFFER_DATA (multifilesink->streamheaders[i]),
503 GST_BUFFER_SIZE (multifilesink->streamheaders[i]), 1,
504 multifilesink->file);
506 goto stdio_write_error;
511 ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
512 multifilesink->file);
514 goto stdio_write_error;
518 g_assert_not_reached ();
526 switch (error->code) {
527 case G_FILE_ERROR_NOSPC:{
528 GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
533 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
534 ("Error while writing to file \"%s\".", filename),
535 ("%s", g_strerror (errno)));
538 g_error_free (error);
541 return GST_FLOW_ERROR;
544 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
545 ("Error while writing to file."), (NULL));
546 return GST_FLOW_ERROR;
550 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
552 GstMultiFileSink *multifilesink;
553 GstStructure *structure;
555 multifilesink = GST_MULTI_FILE_SINK (sink);
557 structure = gst_caps_get_structure (caps, 0);
561 value = gst_structure_get_value (structure, "streamheader");
563 if (GST_VALUE_HOLDS_ARRAY (value)) {
566 if (multifilesink->streamheaders) {
567 for (i = 0; i < multifilesink->n_streamheaders; i++) {
568 gst_buffer_unref (multifilesink->streamheaders[i]);
570 g_free (multifilesink->streamheaders);
573 multifilesink->n_streamheaders = gst_value_array_get_size (value);
574 multifilesink->streamheaders =
575 g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
577 for (i = 0; i < multifilesink->n_streamheaders; i++) {
578 multifilesink->streamheaders[i] =
579 gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value
589 gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
593 while (multifilesink->max_files &&
594 multifilesink->n_files >= multifilesink->max_files) {
595 filename = multifilesink->files->data;
598 multifilesink->files = g_slist_delete_link (multifilesink->files,
599 multifilesink->files);
600 multifilesink->n_files -= 1;
605 gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
609 g_return_val_if_fail (multifilesink->file == NULL, FALSE);
611 gst_multi_file_sink_ensure_max_files (multifilesink);
612 filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
613 multifilesink->file = g_fopen (filename, "wb");
614 if (multifilesink->file == NULL) {
619 multifilesink->files = g_slist_append (multifilesink->files, filename);
620 multifilesink->n_files += 1;
626 gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
631 fclose (multifilesink->file);
632 multifilesink->file = NULL;
635 filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
636 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
640 multifilesink->index++;