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 * 2015 Tim-Philipp Müller <tim@centricular.com>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
27 * SECTION:element-multifilesink
28 * @see_also: #GstFileSrc
30 * Write incoming data to a series of sequentially-named files.
32 * This element is usually used with data where each buffer is an
33 * independent unit of data in its own right (e.g. raw video buffers or
34 * encoded JPEG or PNG images) or with streamable container formats such
35 * as MPEG-TS or MPEG-PS.
37 * It is not possible to use this element to create independently playable
38 * mp4 files, use the splitmuxsink element for that instead.
40 * The filename property should contain a string with a \%d placeholder that will
41 * be substituted with the index for each filename.
43 * If the #GstMultiFileSink:post-messages property is #TRUE, it sends an application
45 * <classname>"GstMultiFileSink"</classname> after writing each
48 * The message's structure contains these fields:
53 * <classname>"filename"</classname>:
54 * the filename where the buffer was written.
60 * <classname>"index"</classname>:
61 * the index of the buffer.
67 * <classname>"timestamp"</classname>:
68 * the timestamp of the buffer.
74 * <classname>"stream-time"</classname>:
75 * the stream time of the buffer.
81 * <classname>"running-time"</classname>:
82 * the running_time of the buffer.
88 * <classname>"duration"</classname>:
89 * the duration of the buffer.
95 * <classname>"offset"</classname>:
96 * the offset of the buffer that triggered the message.
102 * <classname>"offset-end"</classname>:
103 * the offset-end of the buffer that triggered the message.
109 * <title>Example launch line</title>
111 * gst-launch-1.0 audiotestsrc ! multifilesink
112 * gst-launch-1.0 videotestsrc ! multifilesink post-messages=true filename="frame%d"
120 #include <gst/base/gstbasetransform.h>
121 #include <gst/video/video.h>
122 #include <glib/gstdio.h>
123 #include "gstmultifilesink.h"
125 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
128 GST_STATIC_CAPS_ANY);
130 GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug);
131 #define GST_CAT_DEFAULT gst_multi_file_sink_debug
133 #define DEFAULT_LOCATION "%05d"
134 #define DEFAULT_INDEX 0
135 #define DEFAULT_POST_MESSAGES FALSE
136 #define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER
137 #define DEFAULT_MAX_FILES 0
138 #define DEFAULT_MAX_FILE_SIZE G_GUINT64_CONSTANT(2*1024*1024*1024)
139 #define DEFAULT_MAX_FILE_DURATION GST_CLOCK_TIME_NONE
140 #define DEFAULT_AGGREGATE_GOPS FALSE
151 PROP_MAX_FILE_DURATION,
155 static void gst_multi_file_sink_finalize (GObject * object);
157 static void gst_multi_file_sink_set_property (GObject * object, guint prop_id,
158 const GValue * value, GParamSpec * pspec);
159 static void gst_multi_file_sink_get_property (GObject * object, guint prop_id,
160 GValue * value, GParamSpec * pspec);
162 static gboolean gst_multi_file_sink_start (GstBaseSink * bsink);
163 static gboolean gst_multi_file_sink_stop (GstBaseSink * sink);
164 static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
166 static GstFlowReturn gst_multi_file_sink_render_list (GstBaseSink * sink,
167 GstBufferList * buffer_list);
168 static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink,
170 static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink *
172 static void gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
174 static void gst_multi_file_sink_ensure_max_files (GstMultiFileSink *
176 static gboolean gst_multi_file_sink_event (GstBaseSink * sink,
179 #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
181 gst_multi_file_sink_next_get_type (void)
183 static GType multi_file_sink_next_type = 0;
184 static const GEnumValue next_types[] = {
185 {GST_MULTI_FILE_SINK_NEXT_BUFFER, "New file for each buffer", "buffer"},
186 {GST_MULTI_FILE_SINK_NEXT_DISCONT, "New file after each discontinuity",
188 {GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, "New file at each key frame "
189 "(Useful for MPEG-TS segmenting)", "key-frame"},
190 {GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT,
191 "New file after a force key unit event", "key-unit-event"},
192 {GST_MULTI_FILE_SINK_NEXT_MAX_SIZE, "New file when the configured maximum "
193 "file size would be exceeded with the next buffer or buffer list",
195 {GST_MULTI_FILE_SINK_NEXT_MAX_DURATION,
196 "New file when the configured maximum "
197 "file duration would be exceeded with the next buffer or buffer list",
202 if (!multi_file_sink_next_type) {
203 multi_file_sink_next_type =
204 g_enum_register_static ("GstMultiFileSinkNext", next_types);
207 return multi_file_sink_next_type;
210 #define gst_multi_file_sink_parent_class parent_class
211 G_DEFINE_TYPE (GstMultiFileSink, gst_multi_file_sink, GST_TYPE_BASE_SINK);
214 gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
216 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
217 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
218 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
220 gobject_class->set_property = gst_multi_file_sink_set_property;
221 gobject_class->get_property = gst_multi_file_sink_get_property;
223 g_object_class_install_property (gobject_class, PROP_LOCATION,
224 g_param_spec_string ("location", "File Location",
225 "Location of the file to write", NULL,
226 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
228 g_object_class_install_property (gobject_class, PROP_INDEX,
229 g_param_spec_int ("index", "Index",
230 "Index to use with location property to create file names. The "
231 "index is incremented by one for each buffer written.",
232 0, G_MAXINT, DEFAULT_INDEX,
233 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
235 * GstMultiFileSink:post-messages:
237 * Post a message on the GstBus for each file.
239 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
240 g_param_spec_boolean ("post-messages", "Post Messages",
241 "Post a message for each file with information of the buffer",
242 DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
244 * GstMultiFileSink:next-file:
246 * When to start a new file.
248 g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
249 g_param_spec_enum ("next-file", "Next File",
250 "When to start a new file",
251 GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
252 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
256 * GstMultiFileSink:max-files:
258 * Maximum number of files to keep on disk. Once the maximum is reached, old
259 * files start to be deleted to make room for new ones.
261 g_object_class_install_property (gobject_class, PROP_MAX_FILES,
262 g_param_spec_uint ("max-files", "Max files",
263 "Maximum number of files to keep on disk. Once the maximum is reached,"
264 "old files start to be deleted to make room for new ones.",
265 0, G_MAXUINT, DEFAULT_MAX_FILES,
266 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
269 * GstMultiFileSink:max-file-size:
271 * Maximum file size before starting a new file in max-size mode.
273 g_object_class_install_property (gobject_class, PROP_MAX_FILE_SIZE,
274 g_param_spec_uint64 ("max-file-size", "Maximum File Size",
275 "Maximum file size before starting a new file in max-size mode",
276 0, G_MAXUINT64, DEFAULT_MAX_FILE_SIZE,
277 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
280 * GstMultiFileSink:max-file-duration:
282 * Maximum file size before starting a new file in max-size mode.
284 g_object_class_install_property (gobject_class, PROP_MAX_FILE_DURATION,
285 g_param_spec_uint64 ("max-file-duration", "Maximum File Duration",
286 "Maximum file duration before starting a new file in max-size mode",
287 0, G_MAXUINT64, DEFAULT_MAX_FILE_DURATION,
288 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
291 * GstMultiFileSink:aggregate-gops:
293 * Whether to aggregate complete GOPs before doing any processing. Set this
294 * to TRUE to make sure each new file starts with a keyframe. This requires
295 * the upstream element to flag buffers containing key units and delta
296 * units correctly. At least the MPEG-PS and MPEG-TS muxers should be doing
301 g_object_class_install_property (gobject_class, PROP_AGGREGATE_GOPS,
302 g_param_spec_boolean ("aggregate-gops", "Aggregate GOPs",
303 "Whether to aggregate GOPs and process them as a whole without "
304 "splitting", DEFAULT_AGGREGATE_GOPS,
305 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
307 gobject_class->finalize = gst_multi_file_sink_finalize;
309 gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_multi_file_sink_start);
310 gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
311 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
312 gstbasesink_class->render_list =
313 GST_DEBUG_FUNCPTR (gst_multi_file_sink_render_list);
314 gstbasesink_class->set_caps =
315 GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
316 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_multi_file_sink_event);
318 GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
319 "multifilesink element");
321 gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
322 gst_element_class_set_static_metadata (gstelement_class, "Multi-File Sink",
324 "Write buffers to a sequentially named set of files",
325 "David Schleef <ds@schleef.org>");
329 gst_multi_file_sink_init (GstMultiFileSink * multifilesink)
331 multifilesink->filename = g_strdup (DEFAULT_LOCATION);
332 multifilesink->index = DEFAULT_INDEX;
333 multifilesink->post_messages = DEFAULT_POST_MESSAGES;
334 multifilesink->max_files = DEFAULT_MAX_FILES;
335 multifilesink->max_file_size = DEFAULT_MAX_FILE_SIZE;
336 multifilesink->max_file_duration = DEFAULT_MAX_FILE_DURATION;
337 multifilesink->files = NULL;
338 multifilesink->n_files = 0;
340 multifilesink->aggregate_gops = DEFAULT_AGGREGATE_GOPS;
341 multifilesink->gop_adapter = NULL;
343 gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
345 multifilesink->next_segment = GST_CLOCK_TIME_NONE;
346 multifilesink->force_key_unit_count = -1;
350 gst_multi_file_sink_finalize (GObject * object)
352 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
354 g_free (sink->filename);
355 g_slist_foreach (sink->files, (GFunc) g_free, NULL);
356 g_slist_free (sink->files);
358 G_OBJECT_CLASS (parent_class)->finalize (object);
362 gst_multi_file_sink_set_location (GstMultiFileSink * sink,
363 const gchar * location)
365 g_free (sink->filename);
366 /* FIXME: validate location to have just one %d */
367 sink->filename = g_strdup (location);
373 gst_multi_file_sink_set_property (GObject * object, guint prop_id,
374 const GValue * value, GParamSpec * pspec)
376 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
380 gst_multi_file_sink_set_location (sink, g_value_get_string (value));
383 sink->index = g_value_get_int (value);
385 case PROP_POST_MESSAGES:
386 sink->post_messages = g_value_get_boolean (value);
389 sink->next_file = g_value_get_enum (value);
392 sink->max_files = g_value_get_uint (value);
394 case PROP_MAX_FILE_SIZE:
395 sink->max_file_size = g_value_get_uint64 (value);
397 case PROP_MAX_FILE_DURATION:
398 sink->max_file_duration = g_value_get_uint64 (value);
400 case PROP_AGGREGATE_GOPS:
401 sink->aggregate_gops = g_value_get_boolean (value);
404 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
410 gst_multi_file_sink_get_property (GObject * object, guint prop_id,
411 GValue * value, GParamSpec * pspec)
413 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
417 g_value_set_string (value, sink->filename);
420 g_value_set_int (value, sink->index);
422 case PROP_POST_MESSAGES:
423 g_value_set_boolean (value, sink->post_messages);
426 g_value_set_enum (value, sink->next_file);
429 g_value_set_uint (value, sink->max_files);
431 case PROP_MAX_FILE_SIZE:
432 g_value_set_uint64 (value, sink->max_file_size);
434 case PROP_MAX_FILE_DURATION:
435 g_value_set_uint64 (value, sink->max_file_duration);
437 case PROP_AGGREGATE_GOPS:
438 g_value_set_boolean (value, sink->aggregate_gops);
441 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
447 gst_multi_file_sink_start (GstBaseSink * bsink)
449 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (bsink);
451 if (sink->aggregate_gops)
452 sink->gop_adapter = gst_adapter_new ();
453 sink->potential_next_gop = NULL;
454 sink->file_pts = GST_CLOCK_TIME_NONE;
460 gst_multi_file_sink_stop (GstBaseSink * sink)
462 GstMultiFileSink *multifilesink;
465 multifilesink = GST_MULTI_FILE_SINK (sink);
467 if (multifilesink->file != NULL) {
468 fclose (multifilesink->file);
469 multifilesink->file = NULL;
472 if (multifilesink->streamheaders) {
473 for (i = 0; i < multifilesink->n_streamheaders; i++) {
474 gst_buffer_unref (multifilesink->streamheaders[i]);
476 g_free (multifilesink->streamheaders);
477 multifilesink->streamheaders = NULL;
480 if (multifilesink->gop_adapter != NULL) {
481 g_object_unref (multifilesink->gop_adapter);
482 multifilesink->gop_adapter = NULL;
485 if (multifilesink->potential_next_gop != NULL) {
486 g_list_free_full (multifilesink->potential_next_gop,
487 (GDestroyNotify) gst_buffer_unref);
488 multifilesink->potential_next_gop = NULL;
491 multifilesink->force_key_unit_count = -1;
498 gst_multi_file_sink_post_message_full (GstMultiFileSink * multifilesink,
499 GstClockTime timestamp, GstClockTime duration, GstClockTime offset,
500 GstClockTime offset_end, GstClockTime running_time,
501 GstClockTime stream_time, const char *filename)
505 if (!multifilesink->post_messages)
508 s = gst_structure_new ("GstMultiFileSink",
509 "filename", G_TYPE_STRING, filename,
510 "index", G_TYPE_INT, multifilesink->index,
511 "timestamp", G_TYPE_UINT64, timestamp,
512 "stream-time", G_TYPE_UINT64, stream_time,
513 "running-time", G_TYPE_UINT64, running_time,
514 "duration", G_TYPE_UINT64, duration,
515 "offset", G_TYPE_UINT64, offset,
516 "offset-end", G_TYPE_UINT64, offset_end, NULL);
518 gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
519 gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
523 gst_multi_file_sink_post_message_from_time (GstMultiFileSink * multifilesink,
524 GstClockTime timestamp, GstClockTime duration, const char *filename)
526 GstClockTime running_time, stream_time;
527 guint64 offset, offset_end;
531 if (!multifilesink->post_messages)
534 segment = &GST_BASE_SINK (multifilesink)->segment;
535 format = segment->format;
540 running_time = gst_segment_to_running_time (segment, format, timestamp);
541 stream_time = gst_segment_to_stream_time (segment, format, timestamp);
543 gst_multi_file_sink_post_message_full (multifilesink, timestamp, duration,
544 offset, offset_end, running_time, stream_time, filename);
548 gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
549 GstBuffer * buffer, const char *filename)
551 GstClockTime duration, timestamp;
552 GstClockTime running_time, stream_time;
553 guint64 offset, offset_end;
557 if (!multifilesink->post_messages)
560 segment = &GST_BASE_SINK (multifilesink)->segment;
561 format = segment->format;
563 timestamp = GST_BUFFER_TIMESTAMP (buffer);
564 duration = GST_BUFFER_DURATION (buffer);
565 offset = GST_BUFFER_OFFSET (buffer);
566 offset_end = GST_BUFFER_OFFSET_END (buffer);
568 running_time = gst_segment_to_running_time (segment, format, timestamp);
569 stream_time = gst_segment_to_stream_time (segment, format, timestamp);
571 gst_multi_file_sink_post_message_full (multifilesink, timestamp, duration,
572 offset, offset_end, running_time, stream_time, filename);
576 gst_multi_file_sink_write_stream_headers (GstMultiFileSink * sink)
580 if (sink->streamheaders == NULL)
583 GST_DEBUG_OBJECT (sink, "Writing stream headers");
585 /* we want to write these at the beginning */
586 g_assert (sink->cur_file_size == 0);
588 for (i = 0; i < sink->n_streamheaders; i++) {
593 hdr = sink->streamheaders[i];
594 gst_buffer_map (hdr, &map, GST_MAP_READ);
595 ret = fwrite (map.data, map.size, 1, sink->file);
596 gst_buffer_unmap (hdr, &map);
601 sink->cur_file_size += map.size;
608 gst_multi_file_sink_write_buffer (GstMultiFileSink * multifilesink,
613 gboolean first_file = TRUE;
615 gst_buffer_map (buffer, &map, GST_MAP_READ);
617 switch (multifilesink->next_file) {
618 case GST_MULTI_FILE_SINK_NEXT_BUFFER:
619 if (multifilesink->files != NULL)
621 if (!gst_multi_file_sink_open_next_file (multifilesink))
622 goto stdio_write_error;
623 if (first_file == FALSE)
624 gst_multi_file_sink_write_stream_headers (multifilesink);
625 GST_DEBUG_OBJECT (multifilesink,
626 "Writing buffer data (%" G_GSIZE_FORMAT " bytes) to new file",
628 ret = fwrite (map.data, map.size, 1, multifilesink->file);
630 gst_multi_file_sink_close_file (multifilesink, NULL);
631 goto stdio_write_error;
634 gst_multi_file_sink_close_file (multifilesink, buffer);
636 case GST_MULTI_FILE_SINK_NEXT_DISCONT:
637 if (GST_BUFFER_IS_DISCONT (buffer)) {
638 if (multifilesink->file)
639 gst_multi_file_sink_close_file (multifilesink, buffer);
642 if (multifilesink->file == NULL) {
643 if (!gst_multi_file_sink_open_next_file (multifilesink))
644 goto stdio_write_error;
647 ret = fwrite (map.data, map.size, 1, multifilesink->file);
649 goto stdio_write_error;
652 case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
653 if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
654 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
655 multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
660 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
661 GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
662 !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
663 if (multifilesink->file) {
665 gst_multi_file_sink_close_file (multifilesink, buffer);
667 multifilesink->next_segment += 10 * GST_SECOND;
670 if (multifilesink->file == NULL) {
671 if (!gst_multi_file_sink_open_next_file (multifilesink))
672 goto stdio_write_error;
675 gst_multi_file_sink_write_stream_headers (multifilesink);
678 ret = fwrite (map.data, map.size, 1, multifilesink->file);
680 goto stdio_write_error;
683 case GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT:
684 if (multifilesink->file == NULL) {
685 if (!gst_multi_file_sink_open_next_file (multifilesink))
686 goto stdio_write_error;
688 /* we don't need to write stream headers here, they will be inserted in
689 * the stream by upstream elements if key unit events have
690 * all_headers=true set
694 ret = fwrite (map.data, map.size, 1, multifilesink->file);
697 goto stdio_write_error;
700 case GST_MULTI_FILE_SINK_NEXT_MAX_SIZE:{
703 new_size = multifilesink->cur_file_size + map.size;
704 if (new_size > multifilesink->max_file_size) {
706 GST_INFO_OBJECT (multifilesink, "current size: %" G_GUINT64_FORMAT
707 ", new_size: %" G_GUINT64_FORMAT ", max. size %" G_GUINT64_FORMAT,
708 multifilesink->cur_file_size, new_size,
709 multifilesink->max_file_size);
711 if (multifilesink->file != NULL) {
713 gst_multi_file_sink_close_file (multifilesink, buffer);
717 if (multifilesink->file == NULL) {
718 if (!gst_multi_file_sink_open_next_file (multifilesink))
719 goto stdio_write_error;
722 gst_multi_file_sink_write_stream_headers (multifilesink);
725 ret = fwrite (map.data, map.size, 1, multifilesink->file);
728 goto stdio_write_error;
730 multifilesink->cur_file_size += map.size;
733 case GST_MULTI_FILE_SINK_NEXT_MAX_DURATION:{
734 GstClockTime new_duration = 0;
736 if (GST_BUFFER_PTS_IS_VALID (buffer)
737 && GST_CLOCK_TIME_IS_VALID (multifilesink->file_pts)) {
738 /* The new duration will extend to this new buffer pts ... */
739 new_duration = GST_BUFFER_PTS (buffer) - multifilesink->file_pts;
740 /* ... and duration (if it has one) */
741 if (GST_BUFFER_DURATION_IS_VALID (buffer))
742 new_duration += GST_BUFFER_DURATION (buffer);
745 if (new_duration > multifilesink->max_file_duration) {
747 GST_INFO_OBJECT (multifilesink,
748 "new_duration: %" G_GUINT64_FORMAT ", max. duration %"
749 G_GUINT64_FORMAT, new_duration, multifilesink->max_file_duration);
751 if (multifilesink->file != NULL) {
753 gst_multi_file_sink_close_file (multifilesink, buffer);
757 if (multifilesink->file == NULL) {
758 if (!gst_multi_file_sink_open_next_file (multifilesink))
759 goto stdio_write_error;
761 multifilesink->file_pts = GST_BUFFER_PTS (buffer);
763 gst_multi_file_sink_write_stream_headers (multifilesink);
766 ret = fwrite (map.data, map.size, 1, multifilesink->file);
769 goto stdio_write_error;
774 g_assert_not_reached ();
777 gst_buffer_unmap (buffer, &map);
784 GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT,
785 ("Error while writing to file."), ("%s", g_strerror (errno)));
788 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
789 ("Error while writing to file."), ("%s", g_strerror (errno)));
791 gst_buffer_unmap (buffer, &map);
792 return GST_FLOW_ERROR;
796 gst_multi_file_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
798 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (bsink);
799 GstFlowReturn flow = GST_FLOW_OK;
800 gboolean key_unit, header;
802 header = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER);
803 key_unit = !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
805 if (sink->aggregate_gops) {
806 GstBuffer *gop_buffer = NULL;
809 avail = gst_adapter_available (sink->gop_adapter);
811 GST_LOG_OBJECT (sink, "aggregate GOP: received %s%s unit buffer: "
813 (key_unit) ? "key" : "delta", (header) ? " header" : "", buffer);
815 /* If it's a header buffer, it might potentially be for the next GOP */
817 GST_LOG_OBJECT (sink, "Accumulating buffer to potential next GOP");
818 sink->potential_next_gop =
819 g_list_append (sink->potential_next_gop, gst_buffer_ref (buffer));
821 if (key_unit && avail > 0) {
822 GstClockTime pts, dts;
823 GST_LOG_OBJECT (sink, "Grabbing pending completed GOP");
824 pts = gst_adapter_prev_pts_at_offset (sink->gop_adapter, 0, NULL);
825 dts = gst_adapter_prev_dts_at_offset (sink->gop_adapter, 0, NULL);
826 gop_buffer = gst_adapter_take_buffer (sink->gop_adapter, avail);
827 GST_BUFFER_PTS (gop_buffer) = pts;
828 GST_BUFFER_DTS (gop_buffer) = dts;
831 /* just accumulate the buffer */
832 if (sink->potential_next_gop) {
834 GST_LOG_OBJECT (sink,
835 "Carrying over pending next GOP data into adapter");
836 /* If we have pending data, put that first in the adapter */
837 for (tmp = sink->potential_next_gop; tmp; tmp = tmp->next) {
838 GstBuffer *tmpb = (GstBuffer *) tmp->data;
839 gst_adapter_push (sink->gop_adapter, tmpb);
841 g_list_free (sink->potential_next_gop);
842 sink->potential_next_gop = NULL;
844 GST_LOG_OBJECT (sink, "storing buffer in adapter");
845 gst_adapter_push (sink->gop_adapter, gst_buffer_ref (buffer));
847 if (gop_buffer != NULL) {
848 GST_DEBUG_OBJECT (sink, "writing out pending GOP, %u bytes", avail);
849 GST_DEBUG_OBJECT (sink,
850 "gop buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
851 " duration:%" GST_TIME_FORMAT,
852 GST_TIME_ARGS (GST_BUFFER_PTS (gop_buffer)),
853 GST_TIME_ARGS (GST_BUFFER_DTS (gop_buffer)),
854 GST_TIME_ARGS (GST_BUFFER_DURATION (gop_buffer)));
855 flow = gst_multi_file_sink_write_buffer (sink, gop_buffer);
856 gst_buffer_unref (gop_buffer);
860 flow = gst_multi_file_sink_write_buffer (sink, buffer);
866 buffer_list_calc_size (GstBuffer ** buf, guint idx, gpointer data)
868 guint *p_size = data;
871 buf_size = gst_buffer_get_size (*buf);
872 GST_TRACE ("buffer %u has size %" G_GSIZE_FORMAT, idx, buf_size);
879 buffer_list_copy_data (GstBuffer ** buf, guint idx, gpointer data)
881 GstBuffer *dest = data;
885 gst_buffer_copy_into (dest, *buf, GST_BUFFER_COPY_METADATA, 0, -1);
887 num = gst_buffer_n_memory (*buf);
888 for (i = 0; i < num; ++i) {
891 mem = gst_buffer_get_memory (*buf, i);
892 gst_buffer_append_memory (dest, mem);
898 /* Our assumption for now is that the buffers in a buffer list should always
899 * end up in the same file. If someone wants different behaviour, they'll just
900 * have to add a property for that. */
902 gst_multi_file_sink_render_list (GstBaseSink * sink, GstBufferList * list)
907 gst_buffer_list_foreach (list, buffer_list_calc_size, &size);
908 GST_LOG_OBJECT (sink, "total size of buffer list %p: %u", list, size);
910 /* copy all buffers in the list into one single buffer, so we can use
911 * the normal render function (FIXME: optimise to avoid the memcpy) */
912 buf = gst_buffer_new ();
913 gst_buffer_list_foreach (list, buffer_list_copy_data, buf);
914 g_assert (gst_buffer_get_size (buf) == size);
916 gst_multi_file_sink_render (sink, buf);
917 gst_buffer_unref (buf);
923 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
925 GstMultiFileSink *multifilesink;
926 GstStructure *structure;
928 multifilesink = GST_MULTI_FILE_SINK (sink);
930 structure = gst_caps_get_structure (caps, 0);
934 value = gst_structure_get_value (structure, "streamheader");
936 if (GST_VALUE_HOLDS_ARRAY (value)) {
939 if (multifilesink->streamheaders) {
940 for (i = 0; i < multifilesink->n_streamheaders; i++) {
941 gst_buffer_unref (multifilesink->streamheaders[i]);
943 g_free (multifilesink->streamheaders);
946 multifilesink->n_streamheaders = gst_value_array_get_size (value);
947 multifilesink->streamheaders =
948 g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
950 for (i = 0; i < multifilesink->n_streamheaders; i++) {
951 multifilesink->streamheaders[i] =
952 gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value
962 gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
966 while (multifilesink->max_files &&
967 multifilesink->n_files >= multifilesink->max_files) {
968 filename = multifilesink->files->data;
971 multifilesink->files = g_slist_delete_link (multifilesink->files,
972 multifilesink->files);
973 multifilesink->n_files -= 1;
978 gst_multi_file_sink_event (GstBaseSink * sink, GstEvent * event)
980 GstMultiFileSink *multifilesink;
983 multifilesink = GST_MULTI_FILE_SINK (sink);
985 switch (GST_EVENT_TYPE (event)) {
986 case GST_EVENT_CUSTOM_DOWNSTREAM:
988 GstClockTime timestamp, duration;
989 GstClockTime running_time, stream_time;
990 guint64 offset, offset_end;
991 gboolean all_headers;
994 if (multifilesink->next_file != GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT ||
995 !gst_video_event_is_force_key_unit (event))
998 gst_video_event_parse_downstream_force_key_unit (event, ×tamp,
999 &stream_time, &running_time, &all_headers, &count);
1001 if (multifilesink->force_key_unit_count != -1 &&
1002 multifilesink->force_key_unit_count == count)
1005 multifilesink->force_key_unit_count = count;
1007 if (multifilesink->file) {
1008 duration = GST_CLOCK_TIME_NONE;
1009 offset = offset_end = -1;
1010 filename = g_strdup_printf (multifilesink->filename,
1011 multifilesink->index);
1013 gst_multi_file_sink_close_file (multifilesink, NULL);
1015 gst_multi_file_sink_post_message_full (multifilesink, timestamp,
1016 duration, offset, offset_end, running_time, stream_time, filename);
1020 if (multifilesink->file == NULL) {
1021 if (!gst_multi_file_sink_open_next_file (multifilesink))
1022 goto stdio_write_error;
1028 if (multifilesink->aggregate_gops) {
1029 GstBuffer *buf = gst_buffer_new ();
1031 /* push key unit buffer to force writing out the pending GOP data */
1032 GST_INFO_OBJECT (sink, "EOS, write pending GOP data");
1033 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1034 gst_multi_file_sink_render (sink, buf);
1035 gst_buffer_unref (buf);
1037 if (multifilesink->file) {
1040 filename = g_strdup_printf (multifilesink->filename,
1041 multifilesink->index);
1043 gst_multi_file_sink_close_file (multifilesink, NULL);
1045 gst_multi_file_sink_post_message_from_time (multifilesink,
1046 GST_BASE_SINK (multifilesink)->segment.position, -1, filename);
1055 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1060 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
1061 ("Error while writing to file."), (NULL));
1062 gst_event_unref (event);
1068 gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
1072 g_return_val_if_fail (multifilesink->file == NULL, FALSE);
1074 gst_multi_file_sink_ensure_max_files (multifilesink);
1075 filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
1076 multifilesink->file = g_fopen (filename, "wb");
1077 if (multifilesink->file == NULL) {
1082 GST_INFO_OBJECT (multifilesink, "opening file %s", filename);
1083 multifilesink->files = g_slist_append (multifilesink->files, filename);
1084 multifilesink->n_files += 1;
1086 multifilesink->cur_file_size = 0;
1091 gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
1096 fclose (multifilesink->file);
1097 multifilesink->file = NULL;
1100 filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
1101 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
1105 multifilesink->index++;