blob: e11e7094318b567f45b6fbc7e80e60eb2048012f [file] [log] [blame]
David Schleef1126a882006-11-10 18:51:10 +00001/* 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 *
7 * gstmultifilesink.c:
8 *
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.
13 *
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.
18 *
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.
23 */
24/**
25 * SECTION:element-multifilesink
David Schleef1126a882006-11-10 18:51:10 +000026 * @see_also: #GstFileSrc
27 *
David Schleefa0884802007-10-25 23:42:52 +000028 * Write incoming data to a series of sequentially-named files.
Wim Taymansf68cd7e2009-09-11 12:16:18 +020029 *
30 * The filename property should contain a string with a %d placeholder that will
31 * be substituted with the index for each filename.
32 *
Wim Taymans19354832009-09-11 13:12:54 +020033 * If the #GstMultiFileSink:post-messages property is #TRUE, it sends an application
Wim Taymansf68cd7e2009-09-11 12:16:18 +020034 * message named
35 * <classname>&quot;GstMultiFileSink&quot;</classname> after writing each
36 * buffer.
37 *
38 * The message's structure contains these fields:
39 * <itemizedlist>
40 * <listitem>
41 * <para>
42 * #gchar *
43 * <classname>&quot;filename&quot;</classname>:
44 * the filename where the buffer was written.
45 * </para>
46 * </listitem>
47 * <listitem>
48 * <para>
49 * #gint
50 * <classname>&quot;index&quot;</classname>:
51 * the index of the buffer.
52 * </para>
53 * </listitem>
54 * <listitem>
55 * <para>
56 * #GstClockTime
57 * <classname>&quot;timestamp&quot;</classname>:
58 * the timestamp of the buffer.
59 * </para>
60 * </listitem>
61 * <listitem>
62 * <para>
63 * #GstClockTime
64 * <classname>&quot;stream-time&quot;</classname>:
65 * the stream time of the buffer.
66 * </para>
67 * </listitem>
68 * <listitem>
69 * <para>
70 * #GstClockTime
71 * <classname>&quot;running-time&quot;</classname>:
72 * the running_time of the buffer.
73 * </para>
74 * </listitem>
75 * <listitem>
76 * <para>
77 * #GstClockTime
78 * <classname>&quot;duration&quot;</classname>:
79 * the duration of the buffer.
80 * </para>
81 * </listitem>
82 * <listitem>
83 * <para>
84 * #guint64
85 * <classname>&quot;offset&quot;</classname>:
86 * the offset of the buffer that triggered the message.
87 * </para>
88 * </listitem>
89 * <listitem>
90 * <para>
91 * #guint64
92 * <classname>&quot;offset-end&quot;</classname>:
93 * the offset-end of the buffer that triggered the message.
94 * </para>
95 * </listitem>
96 * </itemizedlist>
97 *
98 * <title>Example launch line</title>
99 * <refsect2>
100 * |[
101 * gst-launch audiotestsrc ! multifilesink
Wim Taymans19354832009-09-11 13:12:54 +0200102 * gst-launch videotestsrc ! multifilesink post-messages=true filename="frame%d"
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200103 * ]|
104 * </refsect2>
105 *
106 * Last reviewed on 2009-09-11 (0.10.17)
David Schleef1126a882006-11-10 18:51:10 +0000107 */
108
109#ifdef HAVE_CONFIG_H
110# include "config.h"
111#endif
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200112#include <gst/base/gstbasetransform.h>
David Schleef78eeb662009-09-13 12:30:34 -0700113#include <glib/gstdio.h>
Stefan Kost6f765c52007-07-03 08:01:18 +0000114#include "gstmultifilesink.h"
David Schleef1126a882006-11-10 18:51:10 +0000115
116static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
117 GST_PAD_SINK,
118 GST_PAD_ALWAYS,
119 GST_STATIC_CAPS_ANY);
120
121GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug);
122#define GST_CAT_DEFAULT gst_multi_file_sink_debug
123
124static const GstElementDetails gst_multi_file_sink_details =
125GST_ELEMENT_DETAILS ("Multi-File Sink",
126 "Sink/File",
David Schleefda83e9f2008-02-08 03:44:12 +0000127 "Write buffers to a sequentially named set of files",
David Schleef1126a882006-11-10 18:51:10 +0000128 "David Schleef <ds@schleef.org>");
129
David Schleefa0884802007-10-25 23:42:52 +0000130#define DEFAULT_LOCATION "%05d"
131#define DEFAULT_INDEX 0
Wim Taymans19354832009-09-11 13:12:54 +0200132#define DEFAULT_POST_MESSAGES FALSE
David Schleef78eeb662009-09-13 12:30:34 -0700133#define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER
David Schleefa0884802007-10-25 23:42:52 +0000134
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200135enum
136{
137 PROP_0,
138 PROP_LOCATION,
139 PROP_INDEX,
Wim Taymans19354832009-09-11 13:12:54 +0200140 PROP_POST_MESSAGES,
David Schleef78eeb662009-09-13 12:30:34 -0700141 PROP_NEXT_FILE,
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200142 PROP_LAST
143};
144
145static void gst_multi_file_sink_finalize (GObject * object);
David Schleef1126a882006-11-10 18:51:10 +0000146
147static void gst_multi_file_sink_set_property (GObject * object, guint prop_id,
148 const GValue * value, GParamSpec * pspec);
149static void gst_multi_file_sink_get_property (GObject * object, guint prop_id,
150 GValue * value, GParamSpec * pspec);
151
David Schleef78eeb662009-09-13 12:30:34 -0700152static gboolean gst_multi_file_sink_stop (GstBaseSink * sink);
David Schleef1126a882006-11-10 18:51:10 +0000153static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
154 GstBuffer * buffer);
155
David Schleef78eeb662009-09-13 12:30:34 -0700156#define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
157static GType
158gst_multi_file_sink_next_get_type (void)
159{
160 static GType multi_file_sync_next_type = 0;
161 static const GEnumValue next_types[] = {
162 {GST_MULTI_FILE_SINK_NEXT_BUFFER, "New file for each buffer", "buffer"},
163 {GST_MULTI_FILE_SINK_NEXT_DISCONT, "New file after each discontinuity",
164 "discont"},
165 {0, NULL, NULL}
166 };
167
168 if (!multi_file_sync_next_type) {
169 multi_file_sync_next_type =
170 g_enum_register_static ("GstMultiFileSinkNext", next_types);
171 }
172
173 return multi_file_sync_next_type;
174}
175
David Schleef1126a882006-11-10 18:51:10 +0000176GST_BOILERPLATE (GstMultiFileSink, gst_multi_file_sink, GstBaseSink,
177 GST_TYPE_BASE_SINK);
178
179static void
180gst_multi_file_sink_base_init (gpointer g_class)
181{
182 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
183
184 GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
185 "multifilesink element");
186
187 gst_element_class_add_pad_template (gstelement_class,
188 gst_static_pad_template_get (&sinktemplate));
189 gst_element_class_set_details (gstelement_class,
190 &gst_multi_file_sink_details);
191}
192
193static void
194gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
195{
196 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
197 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
198
199 gobject_class->set_property = gst_multi_file_sink_set_property;
200 gobject_class->get_property = gst_multi_file_sink_get_property;
201
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200202 g_object_class_install_property (gobject_class, PROP_LOCATION,
David Schleef1126a882006-11-10 18:51:10 +0000203 g_param_spec_string ("location", "File Location",
204 "Location of the file to write", NULL, G_PARAM_READWRITE));
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200205
206 g_object_class_install_property (gobject_class, PROP_INDEX,
David Schleefa0884802007-10-25 23:42:52 +0000207 g_param_spec_int ("index", "Index",
208 "Index to use with location property to create file names. The "
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200209 "index is incremented by one for each buffer written.",
210 0, G_MAXINT, DEFAULT_INDEX, G_PARAM_READWRITE));
211 /**
Wim Taymans19354832009-09-11 13:12:54 +0200212 * GstMultiFileSink:post-messages
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200213 *
214 * Post a message on the GstBus for each file.
215 *
216 * Since: 0.10.17
217 */
Wim Taymans19354832009-09-11 13:12:54 +0200218 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
219 g_param_spec_boolean ("post-messages", "Post Messages",
220 "Post a message for each file with information of the buffer",
221 DEFAULT_POST_MESSAGES, G_PARAM_READWRITE));
David Schleef78eeb662009-09-13 12:30:34 -0700222 /**
223 * GstMultiFileSink:next-file
224 *
225 * When to start a new file.
226 *
227 * Since: 0.10.17
228 */
229 g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
230 g_param_spec_enum ("next-file", "Next File",
231 "When to start a new file",
232 GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
233 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
David Schleef1126a882006-11-10 18:51:10 +0000234
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200235 gobject_class->finalize = gst_multi_file_sink_finalize;
David Schleef1126a882006-11-10 18:51:10 +0000236
237 gstbasesink_class->get_times = NULL;
David Schleef78eeb662009-09-13 12:30:34 -0700238 gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
David Schleef1126a882006-11-10 18:51:10 +0000239 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
David Schleef1126a882006-11-10 18:51:10 +0000240}
241
242static void
David Schleefa0884802007-10-25 23:42:52 +0000243gst_multi_file_sink_init (GstMultiFileSink * multifilesink,
David Schleef1126a882006-11-10 18:51:10 +0000244 GstMultiFileSinkClass * g_class)
245{
David Schleefa0884802007-10-25 23:42:52 +0000246 multifilesink->filename = g_strdup (DEFAULT_LOCATION);
247 multifilesink->index = DEFAULT_INDEX;
Wim Taymans19354832009-09-11 13:12:54 +0200248 multifilesink->post_messages = DEFAULT_POST_MESSAGES;
David Schleef1126a882006-11-10 18:51:10 +0000249
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200250 gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
David Schleef1126a882006-11-10 18:51:10 +0000251}
252
253static void
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200254gst_multi_file_sink_finalize (GObject * object)
David Schleef1126a882006-11-10 18:51:10 +0000255{
256 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
257
David Schleef1126a882006-11-10 18:51:10 +0000258 g_free (sink->filename);
Stefan Kosta67ced82007-09-24 10:53:36 +0000259
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200260 G_OBJECT_CLASS (parent_class)->finalize (object);
David Schleef1126a882006-11-10 18:51:10 +0000261}
262
263static gboolean
264gst_multi_file_sink_set_location (GstMultiFileSink * sink,
265 const gchar * location)
266{
267 g_free (sink->filename);
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200268 /* FIXME: validate location to have just one %d */
269 sink->filename = g_strdup (location);
David Schleef1126a882006-11-10 18:51:10 +0000270
271 return TRUE;
272}
Stefan Kosta99d3f82009-01-28 12:29:42 +0200273
David Schleef1126a882006-11-10 18:51:10 +0000274static void
275gst_multi_file_sink_set_property (GObject * object, guint prop_id,
276 const GValue * value, GParamSpec * pspec)
277{
278 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
279
280 switch (prop_id) {
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200281 case PROP_LOCATION:
David Schleef1126a882006-11-10 18:51:10 +0000282 gst_multi_file_sink_set_location (sink, g_value_get_string (value));
283 break;
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200284 case PROP_INDEX:
David Schleefa0884802007-10-25 23:42:52 +0000285 sink->index = g_value_get_int (value);
286 break;
Wim Taymans19354832009-09-11 13:12:54 +0200287 case PROP_POST_MESSAGES:
288 sink->post_messages = g_value_get_boolean (value);
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200289 break;
David Schleef78eeb662009-09-13 12:30:34 -0700290 case PROP_NEXT_FILE:
291 sink->next_file = g_value_get_enum (value);
292 break;
David Schleef1126a882006-11-10 18:51:10 +0000293 default:
294 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
295 break;
296 }
297}
298
299static void
300gst_multi_file_sink_get_property (GObject * object, guint prop_id,
301 GValue * value, GParamSpec * pspec)
302{
303 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
304
305 switch (prop_id) {
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200306 case PROP_LOCATION:
David Schleef1126a882006-11-10 18:51:10 +0000307 g_value_set_string (value, sink->filename);
308 break;
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200309 case PROP_INDEX:
David Schleefa0884802007-10-25 23:42:52 +0000310 g_value_set_int (value, sink->index);
311 break;
Wim Taymans19354832009-09-11 13:12:54 +0200312 case PROP_POST_MESSAGES:
313 g_value_set_boolean (value, sink->post_messages);
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200314 break;
David Schleef78eeb662009-09-13 12:30:34 -0700315 case PROP_NEXT_FILE:
316 g_value_set_enum (value, sink->next_file);
317 break;
David Schleef1126a882006-11-10 18:51:10 +0000318 default:
319 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
320 break;
321 }
322}
323
David Schleef78eeb662009-09-13 12:30:34 -0700324static gboolean
325gst_multi_file_sink_stop (GstBaseSink * sink)
David Schleef1126a882006-11-10 18:51:10 +0000326{
David Schleefa0884802007-10-25 23:42:52 +0000327 GstMultiFileSink *multifilesink;
David Schleef1126a882006-11-10 18:51:10 +0000328
David Schleefa0884802007-10-25 23:42:52 +0000329 multifilesink = GST_MULTI_FILE_SINK (sink);
David Schleef1126a882006-11-10 18:51:10 +0000330
David Schleef78eeb662009-09-13 12:30:34 -0700331 if (multifilesink->file != NULL) {
332 fclose (multifilesink->file);
333 multifilesink->file = NULL;
334 }
David Schleef1126a882006-11-10 18:51:10 +0000335
David Schleef78eeb662009-09-13 12:30:34 -0700336 return TRUE;
337}
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200338
David Schleef78eeb662009-09-13 12:30:34 -0700339
340static void
341gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
342 GstBuffer * buffer, const char *filename)
343{
Wim Taymans19354832009-09-11 13:12:54 +0200344 if (multifilesink->post_messages) {
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200345 GstClockTime duration, timestamp;
346 GstClockTime running_time, stream_time;
347 guint64 offset, offset_end;
348 GstStructure *s;
349 GstSegment *segment;
350 GstFormat format;
351
David Schleef78eeb662009-09-13 12:30:34 -0700352 segment = &GST_BASE_SINK (multifilesink)->segment;
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200353 format = segment->format;
354
355 timestamp = GST_BUFFER_TIMESTAMP (buffer);
356 duration = GST_BUFFER_DURATION (buffer);
357 offset = GST_BUFFER_OFFSET (buffer);
358 offset_end = GST_BUFFER_OFFSET_END (buffer);
359
360 running_time = gst_segment_to_running_time (segment, format, timestamp);
361 stream_time = gst_segment_to_stream_time (segment, format, timestamp);
362
363 s = gst_structure_new ("GstMultiFileSink",
364 "filename", G_TYPE_STRING, filename,
365 "index", G_TYPE_INT, multifilesink->index,
366 "timestamp", G_TYPE_UINT64, timestamp,
367 "stream-time", G_TYPE_UINT64, stream_time,
368 "running-time", G_TYPE_UINT64, running_time,
369 "duration", G_TYPE_UINT64, duration,
370 "offset", G_TYPE_UINT64, offset,
371 "offset-end", G_TYPE_UINT64, offset_end, NULL);
372
373 gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
374 gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
David Schleef1126a882006-11-10 18:51:10 +0000375 }
David Schleef78eeb662009-09-13 12:30:34 -0700376}
David Schleef1126a882006-11-10 18:51:10 +0000377
David Schleef78eeb662009-09-13 12:30:34 -0700378static GstFlowReturn
379gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
380{
381 GstMultiFileSink *multifilesink;
382 guint size;
383 guint8 *data;
384 gchar *filename;
385 gboolean ret;
386 GError *error = NULL;
387
388 size = GST_BUFFER_SIZE (buffer);
389 data = GST_BUFFER_DATA (buffer);
390
391 multifilesink = GST_MULTI_FILE_SINK (sink);
392
393 switch (multifilesink->next_file) {
394 case GST_MULTI_FILE_SINK_NEXT_BUFFER:
395 filename = g_strdup_printf (multifilesink->filename,
396 multifilesink->index);
397
398 ret = g_file_set_contents (filename, (char *) data, size, &error);
399 if (!ret)
400 goto write_error;
401
402 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
403 multifilesink->index++;
404
405 g_free (filename);
406 break;
407 case GST_MULTI_FILE_SINK_NEXT_DISCONT:
408 if (GST_BUFFER_IS_DISCONT (buffer)) {
409 if (multifilesink->file) {
410 fclose (multifilesink->file);
411 multifilesink->file = NULL;
412
413 filename = g_strdup_printf (multifilesink->filename,
414 multifilesink->index);
415 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
416 g_free (filename);
417 multifilesink->index++;
418 }
419 }
420
421 if (multifilesink->file == NULL) {
422 filename = g_strdup_printf (multifilesink->filename,
423 multifilesink->index);
424 multifilesink->file = g_fopen (filename, "wb");
425 g_free (filename);
426
427 if (multifilesink->file == NULL)
428 goto stdio_write_error;
429 }
430
431 ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
432 multifilesink->file);
433 if (ret != 1)
434 goto stdio_write_error;
435
436 break;
437 default:
438 g_assert_not_reached ();
439 }
David Schleef1126a882006-11-10 18:51:10 +0000440
Wim Taymansf68cd7e2009-09-11 12:16:18 +0200441 return GST_FLOW_OK;
442
443 /* ERRORS */
444write_error:
445 {
446 switch (error->code) {
447 case G_FILE_ERROR_NOSPC:{
448 GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
449 (NULL));
450 break;
451 }
452 default:{
453 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
454 ("Error while writing to file \"%s\".", filename),
455 ("%s", g_strerror (errno)));
456 }
457 }
458 g_error_free (error);
459 g_free (filename);
460
461 return GST_FLOW_ERROR;
462 }
David Schleef78eeb662009-09-13 12:30:34 -0700463stdio_write_error:
464 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
465 ("Error while writing to file."), (NULL));
466 return GST_FLOW_ERROR;
David Schleef1126a882006-11-10 18:51:10 +0000467}