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
139 static void gst_multi_file_sink_finalize (GObject * object);
141 static void gst_multi_file_sink_set_property (GObject * object, guint prop_id,
142 const GValue * value, GParamSpec * pspec);
143 static void gst_multi_file_sink_get_property (GObject * object, guint prop_id,
144 GValue * value, GParamSpec * pspec);
146 static gboolean gst_multi_file_sink_stop (GstBaseSink * sink);
147 static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
149 static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink,
152 #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
154 gst_multi_file_sink_next_get_type (void)
156 static GType multi_file_sync_next_type = 0;
157 static const GEnumValue next_types[] = {
158 {GST_MULTI_FILE_SINK_NEXT_BUFFER, "New file for each buffer", "buffer"},
159 {GST_MULTI_FILE_SINK_NEXT_DISCONT, "New file after each discontinuity",
161 {GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, "New file at each key frame "
162 "(Useful for MPEG-TS segmenting)", "key-frame"},
166 if (!multi_file_sync_next_type) {
167 multi_file_sync_next_type =
168 g_enum_register_static ("GstMultiFileSinkNext", next_types);
171 return multi_file_sync_next_type;
174 GST_BOILERPLATE (GstMultiFileSink, gst_multi_file_sink, GstBaseSink,
178 gst_multi_file_sink_base_init (gpointer g_class)
180 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
182 GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
183 "multifilesink element");
185 gst_element_class_add_pad_template (gstelement_class,
186 gst_static_pad_template_get (&sinktemplate));
187 gst_element_class_set_details_simple (gstelement_class, "Multi-File Sink",
189 "Write buffers to a sequentially named set of files",
190 "David Schleef <ds@schleef.org>");
194 gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
196 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
197 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
199 gobject_class->set_property = gst_multi_file_sink_set_property;
200 gobject_class->get_property = gst_multi_file_sink_get_property;
202 g_object_class_install_property (gobject_class, PROP_LOCATION,
203 g_param_spec_string ("location", "File Location",
204 "Location of the file to write", NULL,
205 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
207 g_object_class_install_property (gobject_class, PROP_INDEX,
208 g_param_spec_int ("index", "Index",
209 "Index to use with location property to create file names. The "
210 "index is incremented by one for each buffer written.",
211 0, G_MAXINT, DEFAULT_INDEX,
212 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
214 * GstMultiFileSink:post-messages
216 * Post a message on the GstBus for each file.
220 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
221 g_param_spec_boolean ("post-messages", "Post Messages",
222 "Post a message for each file with information of the buffer",
223 DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
225 * GstMultiFileSink:next-file
227 * When to start a new file.
231 g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
232 g_param_spec_enum ("next-file", "Next File",
233 "When to start a new file",
234 GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
235 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
237 gobject_class->finalize = gst_multi_file_sink_finalize;
239 gstbasesink_class->get_times = NULL;
240 gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
241 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
242 gstbasesink_class->set_caps =
243 GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
247 gst_multi_file_sink_init (GstMultiFileSink * multifilesink,
248 GstMultiFileSinkClass * g_class)
250 multifilesink->filename = g_strdup (DEFAULT_LOCATION);
251 multifilesink->index = DEFAULT_INDEX;
252 multifilesink->post_messages = DEFAULT_POST_MESSAGES;
254 gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
256 multifilesink->next_segment = GST_CLOCK_TIME_NONE;
260 gst_multi_file_sink_finalize (GObject * object)
262 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
264 g_free (sink->filename);
266 G_OBJECT_CLASS (parent_class)->finalize (object);
270 gst_multi_file_sink_set_location (GstMultiFileSink * sink,
271 const gchar * location)
273 g_free (sink->filename);
274 /* FIXME: validate location to have just one %d */
275 sink->filename = g_strdup (location);
281 gst_multi_file_sink_set_property (GObject * object, guint prop_id,
282 const GValue * value, GParamSpec * pspec)
284 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
288 gst_multi_file_sink_set_location (sink, g_value_get_string (value));
291 sink->index = g_value_get_int (value);
293 case PROP_POST_MESSAGES:
294 sink->post_messages = g_value_get_boolean (value);
297 sink->next_file = g_value_get_enum (value);
300 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
306 gst_multi_file_sink_get_property (GObject * object, guint prop_id,
307 GValue * value, GParamSpec * pspec)
309 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
313 g_value_set_string (value, sink->filename);
316 g_value_set_int (value, sink->index);
318 case PROP_POST_MESSAGES:
319 g_value_set_boolean (value, sink->post_messages);
322 g_value_set_enum (value, sink->next_file);
325 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
331 gst_multi_file_sink_stop (GstBaseSink * sink)
333 GstMultiFileSink *multifilesink;
336 multifilesink = GST_MULTI_FILE_SINK (sink);
338 if (multifilesink->file != NULL) {
339 fclose (multifilesink->file);
340 multifilesink->file = NULL;
343 if (multifilesink->streamheaders) {
344 for (i = 0; i < multifilesink->n_streamheaders; i++) {
345 gst_buffer_unref (multifilesink->streamheaders[i]);
347 g_free (multifilesink->streamheaders);
355 gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
356 GstBuffer * buffer, const char *filename)
358 if (multifilesink->post_messages) {
359 GstClockTime duration, timestamp;
360 GstClockTime running_time, stream_time;
361 guint64 offset, offset_end;
366 segment = &GST_BASE_SINK (multifilesink)->segment;
367 format = segment->format;
369 timestamp = GST_BUFFER_TIMESTAMP (buffer);
370 duration = GST_BUFFER_DURATION (buffer);
371 offset = GST_BUFFER_OFFSET (buffer);
372 offset_end = GST_BUFFER_OFFSET_END (buffer);
374 running_time = gst_segment_to_running_time (segment, format, timestamp);
375 stream_time = gst_segment_to_stream_time (segment, format, timestamp);
377 s = gst_structure_new ("GstMultiFileSink",
378 "filename", G_TYPE_STRING, filename,
379 "index", G_TYPE_INT, multifilesink->index,
380 "timestamp", G_TYPE_UINT64, timestamp,
381 "stream-time", G_TYPE_UINT64, stream_time,
382 "running-time", G_TYPE_UINT64, running_time,
383 "duration", G_TYPE_UINT64, duration,
384 "offset", G_TYPE_UINT64, offset,
385 "offset-end", G_TYPE_UINT64, offset_end, NULL);
387 gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
388 gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
393 gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
395 GstMultiFileSink *multifilesink;
400 GError *error = NULL;
402 size = GST_BUFFER_SIZE (buffer);
403 data = GST_BUFFER_DATA (buffer);
405 multifilesink = GST_MULTI_FILE_SINK (sink);
407 switch (multifilesink->next_file) {
408 case GST_MULTI_FILE_SINK_NEXT_BUFFER:
409 filename = g_strdup_printf (multifilesink->filename,
410 multifilesink->index);
412 ret = g_file_set_contents (filename, (char *) data, size, &error);
416 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
417 multifilesink->index++;
421 case GST_MULTI_FILE_SINK_NEXT_DISCONT:
422 if (GST_BUFFER_IS_DISCONT (buffer)) {
423 if (multifilesink->file) {
424 fclose (multifilesink->file);
425 multifilesink->file = NULL;
427 filename = g_strdup_printf (multifilesink->filename,
428 multifilesink->index);
429 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
431 multifilesink->index++;
435 if (multifilesink->file == NULL) {
436 filename = g_strdup_printf (multifilesink->filename,
437 multifilesink->index);
438 multifilesink->file = g_fopen (filename, "wb");
441 if (multifilesink->file == NULL)
442 goto stdio_write_error;
445 ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
446 multifilesink->file);
448 goto stdio_write_error;
451 case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
452 if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
453 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
454 multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
459 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
460 GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
461 !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
462 if (multifilesink->file) {
463 fclose (multifilesink->file);
464 multifilesink->file = NULL;
466 filename = g_strdup_printf (multifilesink->filename,
467 multifilesink->index);
468 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
470 multifilesink->index++;
473 multifilesink->next_segment += 10 * GST_SECOND;
476 if (multifilesink->file == NULL) {
479 filename = g_strdup_printf (multifilesink->filename,
480 multifilesink->index);
481 multifilesink->file = g_fopen (filename, "wb");
484 if (multifilesink->file == NULL)
485 goto stdio_write_error;
487 if (multifilesink->streamheaders) {
488 for (i = 0; i < multifilesink->n_streamheaders; i++) {
489 ret = fwrite (GST_BUFFER_DATA (multifilesink->streamheaders[i]),
490 GST_BUFFER_SIZE (multifilesink->streamheaders[i]), 1,
491 multifilesink->file);
493 goto stdio_write_error;
498 ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
499 multifilesink->file);
501 goto stdio_write_error;
505 g_assert_not_reached ();
513 switch (error->code) {
514 case G_FILE_ERROR_NOSPC:{
515 GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
520 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
521 ("Error while writing to file \"%s\".", filename),
522 ("%s", g_strerror (errno)));
525 g_error_free (error);
528 return GST_FLOW_ERROR;
531 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
532 ("Error while writing to file."), (NULL));
533 return GST_FLOW_ERROR;
537 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
539 GstMultiFileSink *multifilesink;
540 GstStructure *structure;
542 multifilesink = GST_MULTI_FILE_SINK (sink);
544 structure = gst_caps_get_structure (caps, 0);
548 value = gst_structure_get_value (structure, "streamheader");
550 if (GST_VALUE_HOLDS_ARRAY (value)) {
553 if (multifilesink->streamheaders) {
554 for (i = 0; i < multifilesink->n_streamheaders; i++) {
555 gst_buffer_unref (multifilesink->streamheaders[i]);
557 g_free (multifilesink->streamheaders);
560 multifilesink->n_streamheaders = gst_value_array_get_size (value);
561 multifilesink->streamheaders =
562 g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
564 for (i = 0; i < multifilesink->n_streamheaders; i++) {
565 multifilesink->streamheaders[i] =
566 gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value