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 location="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_add_old_file (GstMultiFileSink * multifilesink,
176 static void gst_multi_file_sink_ensure_max_files (GstMultiFileSink *
178 static gboolean gst_multi_file_sink_event (GstBaseSink * sink,
181 #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
183 gst_multi_file_sink_next_get_type (void)
185 static GType multi_file_sink_next_type = 0;
186 static const GEnumValue next_types[] = {
187 {GST_MULTI_FILE_SINK_NEXT_BUFFER, "New file for each buffer", "buffer"},
188 {GST_MULTI_FILE_SINK_NEXT_DISCONT, "New file after each discontinuity",
190 {GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, "New file at each key frame "
191 "(Useful for MPEG-TS segmenting)", "key-frame"},
192 {GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT,
193 "New file after a force key unit event", "key-unit-event"},
194 {GST_MULTI_FILE_SINK_NEXT_MAX_SIZE, "New file when the configured maximum "
195 "file size would be exceeded with the next buffer or buffer list",
197 {GST_MULTI_FILE_SINK_NEXT_MAX_DURATION,
198 "New file when the configured maximum "
199 "file duration would be exceeded with the next buffer or buffer list",
204 if (!multi_file_sink_next_type) {
205 multi_file_sink_next_type =
206 g_enum_register_static ("GstMultiFileSinkNext", next_types);
209 return multi_file_sink_next_type;
212 #define gst_multi_file_sink_parent_class parent_class
213 G_DEFINE_TYPE (GstMultiFileSink, gst_multi_file_sink, GST_TYPE_BASE_SINK);
216 gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
218 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
219 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
220 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
222 gobject_class->set_property = gst_multi_file_sink_set_property;
223 gobject_class->get_property = gst_multi_file_sink_get_property;
225 g_object_class_install_property (gobject_class, PROP_LOCATION,
226 g_param_spec_string ("location", "File Location",
227 "Location of the file to write", NULL,
228 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
230 g_object_class_install_property (gobject_class, PROP_INDEX,
231 g_param_spec_int ("index", "Index",
232 "Index to use with location property to create file names. The "
233 "index is incremented by one for each buffer written.",
234 0, G_MAXINT, DEFAULT_INDEX,
235 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
237 * GstMultiFileSink:post-messages:
239 * Post a message on the GstBus for each file.
241 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
242 g_param_spec_boolean ("post-messages", "Post Messages",
243 "Post a message for each file with information of the buffer",
244 DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
246 * GstMultiFileSink:next-file:
248 * When to start a new file.
250 g_object_class_install_property (gobject_class, PROP_NEXT_FILE,
251 g_param_spec_enum ("next-file", "Next File",
252 "When to start a new file",
253 GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
254 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
258 * GstMultiFileSink:max-files:
260 * Maximum number of files to keep on disk. Once the maximum is reached, old
261 * files start to be deleted to make room for new ones.
263 g_object_class_install_property (gobject_class, PROP_MAX_FILES,
264 g_param_spec_uint ("max-files", "Max files",
265 "Maximum number of files to keep on disk. Once the maximum is reached,"
266 "old files start to be deleted to make room for new ones.",
267 0, G_MAXUINT, DEFAULT_MAX_FILES,
268 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
271 * GstMultiFileSink:max-file-size:
273 * Maximum file size before starting a new file in max-size mode.
275 g_object_class_install_property (gobject_class, PROP_MAX_FILE_SIZE,
276 g_param_spec_uint64 ("max-file-size", "Maximum File Size",
277 "Maximum file size before starting a new file in max-size mode",
278 0, G_MAXUINT64, DEFAULT_MAX_FILE_SIZE,
279 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
282 * GstMultiFileSink:max-file-duration:
284 * Maximum file size before starting a new file in max-size mode.
286 g_object_class_install_property (gobject_class, PROP_MAX_FILE_DURATION,
287 g_param_spec_uint64 ("max-file-duration", "Maximum File Duration",
288 "Maximum file duration before starting a new file in max-size mode "
289 "(in nanoseconds)", 0, G_MAXUINT64, DEFAULT_MAX_FILE_DURATION,
290 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
293 * GstMultiFileSink:aggregate-gops:
295 * Whether to aggregate complete GOPs before doing any processing. Set this
296 * to TRUE to make sure each new file starts with a keyframe. This requires
297 * the upstream element to flag buffers containing key units and delta
298 * units correctly. At least the MPEG-PS and MPEG-TS muxers should be doing
303 g_object_class_install_property (gobject_class, PROP_AGGREGATE_GOPS,
304 g_param_spec_boolean ("aggregate-gops", "Aggregate GOPs",
305 "Whether to aggregate GOPs and process them as a whole without "
306 "splitting", DEFAULT_AGGREGATE_GOPS,
307 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
309 gobject_class->finalize = gst_multi_file_sink_finalize;
311 gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_multi_file_sink_start);
312 gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
313 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
314 gstbasesink_class->render_list =
315 GST_DEBUG_FUNCPTR (gst_multi_file_sink_render_list);
316 gstbasesink_class->set_caps =
317 GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
318 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_multi_file_sink_event);
320 GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
321 "multifilesink element");
323 gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
324 gst_element_class_set_static_metadata (gstelement_class, "Multi-File Sink",
326 "Write buffers to a sequentially named set of files",
327 "David Schleef <ds@schleef.org>");
331 gst_multi_file_sink_init (GstMultiFileSink * multifilesink)
333 multifilesink->filename = g_strdup (DEFAULT_LOCATION);
334 multifilesink->index = DEFAULT_INDEX;
335 multifilesink->post_messages = DEFAULT_POST_MESSAGES;
336 multifilesink->max_files = DEFAULT_MAX_FILES;
337 multifilesink->max_file_size = DEFAULT_MAX_FILE_SIZE;
338 multifilesink->max_file_duration = DEFAULT_MAX_FILE_DURATION;
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);
356 G_OBJECT_CLASS (parent_class)->finalize (object);
360 gst_multi_file_sink_set_location (GstMultiFileSink * sink,
361 const gchar * location)
363 g_free (sink->filename);
364 /* FIXME: validate location to have just one %d */
365 sink->filename = g_strdup (location);
371 gst_multi_file_sink_set_property (GObject * object, guint prop_id,
372 const GValue * value, GParamSpec * pspec)
374 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
378 gst_multi_file_sink_set_location (sink, g_value_get_string (value));
381 sink->index = g_value_get_int (value);
383 case PROP_POST_MESSAGES:
384 sink->post_messages = g_value_get_boolean (value);
387 sink->next_file = g_value_get_enum (value);
390 sink->max_files = g_value_get_uint (value);
392 case PROP_MAX_FILE_SIZE:
393 sink->max_file_size = g_value_get_uint64 (value);
395 case PROP_MAX_FILE_DURATION:
396 sink->max_file_duration = g_value_get_uint64 (value);
398 case PROP_AGGREGATE_GOPS:
399 sink->aggregate_gops = g_value_get_boolean (value);
402 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
408 gst_multi_file_sink_get_property (GObject * object, guint prop_id,
409 GValue * value, GParamSpec * pspec)
411 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
415 g_value_set_string (value, sink->filename);
418 g_value_set_int (value, sink->index);
420 case PROP_POST_MESSAGES:
421 g_value_set_boolean (value, sink->post_messages);
424 g_value_set_enum (value, sink->next_file);
427 g_value_set_uint (value, sink->max_files);
429 case PROP_MAX_FILE_SIZE:
430 g_value_set_uint64 (value, sink->max_file_size);
432 case PROP_MAX_FILE_DURATION:
433 g_value_set_uint64 (value, sink->max_file_duration);
435 case PROP_AGGREGATE_GOPS:
436 g_value_set_boolean (value, sink->aggregate_gops);
439 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
445 gst_multi_file_sink_start (GstBaseSink * bsink)
447 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (bsink);
449 if (sink->aggregate_gops)
450 sink->gop_adapter = gst_adapter_new ();
451 sink->potential_next_gop = NULL;
452 sink->file_pts = GST_CLOCK_TIME_NONE;
454 g_queue_init (&sink->old_files);
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;
493 g_queue_foreach (&multifilesink->old_files, (GFunc) g_free, NULL);
494 g_queue_clear (&multifilesink->old_files);
501 gst_multi_file_sink_post_message_full (GstMultiFileSink * multifilesink,
502 GstClockTime timestamp, GstClockTime duration, GstClockTime offset,
503 GstClockTime offset_end, GstClockTime running_time,
504 GstClockTime stream_time, const char *filename)
508 if (!multifilesink->post_messages)
511 s = gst_structure_new ("GstMultiFileSink",
512 "filename", G_TYPE_STRING, filename,
513 "index", G_TYPE_INT, multifilesink->index,
514 "timestamp", G_TYPE_UINT64, timestamp,
515 "stream-time", G_TYPE_UINT64, stream_time,
516 "running-time", G_TYPE_UINT64, running_time,
517 "duration", G_TYPE_UINT64, duration,
518 "offset", G_TYPE_UINT64, offset,
519 "offset-end", G_TYPE_UINT64, offset_end, NULL);
521 gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
522 gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
526 gst_multi_file_sink_post_message_from_time (GstMultiFileSink * multifilesink,
527 GstClockTime timestamp, GstClockTime duration, const char *filename)
529 GstClockTime running_time, stream_time;
530 guint64 offset, offset_end;
534 if (!multifilesink->post_messages)
537 segment = &GST_BASE_SINK (multifilesink)->segment;
538 format = segment->format;
543 running_time = gst_segment_to_running_time (segment, format, timestamp);
544 stream_time = gst_segment_to_stream_time (segment, format, timestamp);
546 gst_multi_file_sink_post_message_full (multifilesink, timestamp, duration,
547 offset, offset_end, running_time, stream_time, filename);
551 gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
552 GstBuffer * buffer, const char *filename)
554 GstClockTime duration, timestamp;
555 GstClockTime running_time, stream_time;
556 guint64 offset, offset_end;
560 if (!multifilesink->post_messages)
563 segment = &GST_BASE_SINK (multifilesink)->segment;
564 format = segment->format;
566 timestamp = GST_BUFFER_TIMESTAMP (buffer);
567 duration = GST_BUFFER_DURATION (buffer);
568 offset = GST_BUFFER_OFFSET (buffer);
569 offset_end = GST_BUFFER_OFFSET_END (buffer);
571 running_time = gst_segment_to_running_time (segment, format, timestamp);
572 stream_time = gst_segment_to_stream_time (segment, format, timestamp);
574 gst_multi_file_sink_post_message_full (multifilesink, timestamp, duration,
575 offset, offset_end, running_time, stream_time, filename);
579 gst_multi_file_sink_write_stream_headers (GstMultiFileSink * sink)
583 if (sink->streamheaders == NULL)
586 /* we want to write these at the beginning */
587 g_assert (sink->cur_file_size == 0);
589 for (i = 0; i < sink->n_streamheaders; i++) {
594 hdr = sink->streamheaders[i];
595 gst_buffer_map (hdr, &map, GST_MAP_READ);
596 ret = fwrite (map.data, map.size, 1, sink->file);
597 gst_buffer_unmap (hdr, &map);
602 sink->cur_file_size += map.size;
609 gst_multi_file_sink_write_buffer (GstMultiFileSink * multifilesink,
615 GError *error = NULL;
616 gboolean first_file = TRUE;
618 gst_buffer_map (buffer, &map, GST_MAP_READ);
620 switch (multifilesink->next_file) {
621 case GST_MULTI_FILE_SINK_NEXT_BUFFER:
622 gst_multi_file_sink_ensure_max_files (multifilesink);
624 filename = g_strdup_printf (multifilesink->filename,
625 multifilesink->index);
626 ret = g_file_set_contents (filename, (char *) map.data, map.size, &error);
630 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
632 gst_multi_file_sink_add_old_file (multifilesink, filename);
634 multifilesink->index++;
637 case GST_MULTI_FILE_SINK_NEXT_DISCONT:
638 if (GST_BUFFER_IS_DISCONT (buffer)) {
639 if (multifilesink->file)
640 gst_multi_file_sink_close_file (multifilesink, buffer);
643 if (multifilesink->file == NULL) {
644 if (!gst_multi_file_sink_open_next_file (multifilesink))
645 goto stdio_write_error;
648 ret = fwrite (map.data, map.size, 1, multifilesink->file);
650 goto stdio_write_error;
653 case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME:
654 if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) {
655 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
656 multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) +
661 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
662 GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
663 !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
664 if (multifilesink->file) {
666 gst_multi_file_sink_close_file (multifilesink, buffer);
668 multifilesink->next_segment += 10 * GST_SECOND;
671 if (multifilesink->file == NULL) {
672 if (!gst_multi_file_sink_open_next_file (multifilesink))
673 goto stdio_write_error;
676 gst_multi_file_sink_write_stream_headers (multifilesink);
679 ret = fwrite (map.data, map.size, 1, multifilesink->file);
681 goto stdio_write_error;
684 case GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT:
685 if (multifilesink->file == NULL) {
686 if (!gst_multi_file_sink_open_next_file (multifilesink))
687 goto stdio_write_error;
689 /* we don't need to write stream headers here, they will be inserted in
690 * the stream by upstream elements if key unit events have
691 * all_headers=true set
695 ret = fwrite (map.data, map.size, 1, multifilesink->file);
698 goto stdio_write_error;
701 case GST_MULTI_FILE_SINK_NEXT_MAX_SIZE:{
704 new_size = multifilesink->cur_file_size + map.size;
705 if (new_size > multifilesink->max_file_size) {
707 GST_INFO_OBJECT (multifilesink, "current size: %" G_GUINT64_FORMAT
708 ", new_size: %" G_GUINT64_FORMAT ", max. size %" G_GUINT64_FORMAT,
709 multifilesink->cur_file_size, new_size,
710 multifilesink->max_file_size);
712 if (multifilesink->file != NULL) {
714 gst_multi_file_sink_close_file (multifilesink, buffer);
718 if (multifilesink->file == NULL) {
719 if (!gst_multi_file_sink_open_next_file (multifilesink))
720 goto stdio_write_error;
723 gst_multi_file_sink_write_stream_headers (multifilesink);
726 ret = fwrite (map.data, map.size, 1, multifilesink->file);
729 goto stdio_write_error;
731 multifilesink->cur_file_size += map.size;
734 case GST_MULTI_FILE_SINK_NEXT_MAX_DURATION:{
735 GstClockTime new_duration = 0;
737 if (GST_BUFFER_PTS_IS_VALID (buffer)
738 && GST_CLOCK_TIME_IS_VALID (multifilesink->file_pts)) {
739 /* The new duration will extend to this new buffer pts ... */
740 new_duration = GST_BUFFER_PTS (buffer) - multifilesink->file_pts;
741 /* ... and duration (if it has one) */
742 if (GST_BUFFER_DURATION_IS_VALID (buffer))
743 new_duration += GST_BUFFER_DURATION (buffer);
746 if (new_duration > multifilesink->max_file_duration) {
748 GST_INFO_OBJECT (multifilesink,
749 "new_duration: %" G_GUINT64_FORMAT ", max. duration %"
750 G_GUINT64_FORMAT, new_duration, multifilesink->max_file_duration);
752 if (multifilesink->file != NULL) {
754 gst_multi_file_sink_close_file (multifilesink, buffer);
758 if (multifilesink->file == NULL) {
759 if (!gst_multi_file_sink_open_next_file (multifilesink))
760 goto stdio_write_error;
762 multifilesink->file_pts = GST_BUFFER_PTS (buffer);
764 gst_multi_file_sink_write_stream_headers (multifilesink);
767 ret = fwrite (map.data, map.size, 1, multifilesink->file);
770 goto stdio_write_error;
775 g_assert_not_reached ();
778 gst_buffer_unmap (buffer, &map);
784 switch (error->code) {
785 case G_FILE_ERROR_NOSPC:{
786 GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL),
791 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
792 ("Error while writing to file \"%s\".", filename),
793 ("%s", g_strerror (errno)));
796 g_error_free (error);
799 gst_buffer_unmap (buffer, &map);
800 return GST_FLOW_ERROR;
805 GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT,
806 ("Error while writing to file."), ("%s", g_strerror (errno)));
809 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
810 ("Error while writing to file."), ("%s", g_strerror (errno)));
812 gst_buffer_unmap (buffer, &map);
813 return GST_FLOW_ERROR;
817 gst_multi_file_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
819 GstMultiFileSink *sink = GST_MULTI_FILE_SINK (bsink);
820 GstFlowReturn flow = GST_FLOW_OK;
821 gboolean key_unit, header;
823 header = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER);
824 key_unit = !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
826 if (sink->aggregate_gops) {
827 GstBuffer *gop_buffer = NULL;
830 avail = gst_adapter_available (sink->gop_adapter);
832 GST_LOG_OBJECT (sink, "aggregate GOP: received %s%s unit buffer: "
834 (key_unit) ? "key" : "delta", (header) ? " header" : "", buffer);
836 /* If it's a header buffer, it might potentially be for the next GOP */
838 GST_LOG_OBJECT (sink, "Accumulating buffer to potential next GOP");
839 sink->potential_next_gop =
840 g_list_append (sink->potential_next_gop, gst_buffer_ref (buffer));
842 if (key_unit && avail > 0) {
843 GstClockTime pts, dts;
844 GST_LOG_OBJECT (sink, "Grabbing pending completed GOP");
845 pts = gst_adapter_prev_pts_at_offset (sink->gop_adapter, 0, NULL);
846 dts = gst_adapter_prev_dts_at_offset (sink->gop_adapter, 0, NULL);
847 gop_buffer = gst_adapter_take_buffer (sink->gop_adapter, avail);
848 GST_BUFFER_PTS (gop_buffer) = pts;
849 GST_BUFFER_DTS (gop_buffer) = dts;
852 /* just accumulate the buffer */
853 if (sink->potential_next_gop) {
855 GST_LOG_OBJECT (sink,
856 "Carrying over pending next GOP data into adapter");
857 /* If we have pending data, put that first in the adapter */
858 for (tmp = sink->potential_next_gop; tmp; tmp = tmp->next) {
859 GstBuffer *tmpb = (GstBuffer *) tmp->data;
860 gst_adapter_push (sink->gop_adapter, tmpb);
862 g_list_free (sink->potential_next_gop);
863 sink->potential_next_gop = NULL;
865 GST_LOG_OBJECT (sink, "storing buffer in adapter");
866 gst_adapter_push (sink->gop_adapter, gst_buffer_ref (buffer));
868 if (gop_buffer != NULL) {
869 GST_DEBUG_OBJECT (sink, "writing out pending GOP, %u bytes", avail);
870 GST_DEBUG_OBJECT (sink,
871 "gop buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
872 " duration:%" GST_TIME_FORMAT,
873 GST_TIME_ARGS (GST_BUFFER_PTS (gop_buffer)),
874 GST_TIME_ARGS (GST_BUFFER_DTS (gop_buffer)),
875 GST_TIME_ARGS (GST_BUFFER_DURATION (gop_buffer)));
876 flow = gst_multi_file_sink_write_buffer (sink, gop_buffer);
877 gst_buffer_unref (gop_buffer);
881 flow = gst_multi_file_sink_write_buffer (sink, buffer);
887 buffer_list_copy_data (GstBuffer ** buf, guint idx, gpointer data)
889 GstBuffer *dest = data;
893 gst_buffer_copy_into (dest, *buf, GST_BUFFER_COPY_METADATA, 0, -1);
895 num = gst_buffer_n_memory (*buf);
896 for (i = 0; i < num; ++i) {
899 mem = gst_buffer_get_memory (*buf, i);
900 gst_buffer_append_memory (dest, mem);
906 /* Our assumption for now is that the buffers in a buffer list should always
907 * end up in the same file. If someone wants different behaviour, they'll just
908 * have to add a property for that. */
910 gst_multi_file_sink_render_list (GstBaseSink * sink, GstBufferList * list)
915 size = gst_buffer_list_calculate_size (list);
916 GST_LOG_OBJECT (sink, "total size of buffer list %p: %u", list, size);
918 /* copy all buffers in the list into one single buffer, so we can use
919 * the normal render function (FIXME: optimise to avoid the memcpy) */
920 buf = gst_buffer_new ();
921 gst_buffer_list_foreach (list, buffer_list_copy_data, buf);
922 g_assert (gst_buffer_get_size (buf) == size);
924 gst_multi_file_sink_render (sink, buf);
925 gst_buffer_unref (buf);
931 gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
933 GstMultiFileSink *multifilesink;
934 GstStructure *structure;
936 multifilesink = GST_MULTI_FILE_SINK (sink);
938 structure = gst_caps_get_structure (caps, 0);
942 value = gst_structure_get_value (structure, "streamheader");
944 if (GST_VALUE_HOLDS_ARRAY (value)) {
947 if (multifilesink->streamheaders) {
948 for (i = 0; i < multifilesink->n_streamheaders; i++) {
949 gst_buffer_unref (multifilesink->streamheaders[i]);
951 g_free (multifilesink->streamheaders);
954 multifilesink->n_streamheaders = gst_value_array_get_size (value);
955 multifilesink->streamheaders =
956 g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders);
958 for (i = 0; i < multifilesink->n_streamheaders; i++) {
959 multifilesink->streamheaders[i] =
960 gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value
969 /* Takes ownership of the filename string */
971 gst_multi_file_sink_add_old_file (GstMultiFileSink * multifilesink, gchar * fn)
973 /* Only add file to the list if a max_files limit is set, otherwise we never
974 * prune the list and memory just builds up until the pipeline is stopped. */
975 if (multifilesink->max_files > 0) {
976 g_queue_push_tail (&multifilesink->old_files, fn);
983 gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
985 guint max_files = multifilesink->max_files;
990 while (g_queue_get_length (&multifilesink->old_files) >= max_files) {
993 filename = g_queue_pop_head (&multifilesink->old_files);
1000 gst_multi_file_sink_event (GstBaseSink * sink, GstEvent * event)
1002 GstMultiFileSink *multifilesink;
1005 multifilesink = GST_MULTI_FILE_SINK (sink);
1007 switch (GST_EVENT_TYPE (event)) {
1008 case GST_EVENT_CUSTOM_DOWNSTREAM:
1010 GstClockTime timestamp, duration;
1011 GstClockTime running_time, stream_time;
1012 guint64 offset, offset_end;
1013 gboolean all_headers;
1016 if (multifilesink->next_file != GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT ||
1017 !gst_video_event_is_force_key_unit (event))
1020 gst_video_event_parse_downstream_force_key_unit (event, ×tamp,
1021 &stream_time, &running_time, &all_headers, &count);
1023 if (multifilesink->force_key_unit_count != -1 &&
1024 multifilesink->force_key_unit_count == count)
1027 multifilesink->force_key_unit_count = count;
1029 if (multifilesink->file) {
1030 duration = GST_CLOCK_TIME_NONE;
1031 offset = offset_end = -1;
1032 filename = g_strdup_printf (multifilesink->filename,
1033 multifilesink->index);
1035 gst_multi_file_sink_close_file (multifilesink, NULL);
1037 gst_multi_file_sink_post_message_full (multifilesink, timestamp,
1038 duration, offset, offset_end, running_time, stream_time, filename);
1042 if (multifilesink->file == NULL) {
1043 if (!gst_multi_file_sink_open_next_file (multifilesink))
1044 goto stdio_write_error;
1050 if (multifilesink->aggregate_gops) {
1051 GstBuffer *buf = gst_buffer_new ();
1053 /* push key unit buffer to force writing out the pending GOP data */
1054 GST_INFO_OBJECT (sink, "EOS, write pending GOP data");
1055 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1056 gst_multi_file_sink_render (sink, buf);
1057 gst_buffer_unref (buf);
1059 if (multifilesink->file) {
1062 filename = g_strdup_printf (multifilesink->filename,
1063 multifilesink->index);
1065 gst_multi_file_sink_close_file (multifilesink, NULL);
1067 gst_multi_file_sink_post_message_from_time (multifilesink,
1068 GST_BASE_SINK (multifilesink)->segment.position, -1, filename);
1077 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1082 GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
1083 ("Error while writing to file."), (NULL));
1084 gst_event_unref (event);
1090 gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
1094 g_return_val_if_fail (multifilesink->file == NULL, FALSE);
1096 gst_multi_file_sink_ensure_max_files (multifilesink);
1098 filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
1099 multifilesink->file = g_fopen (filename, "wb");
1100 if (multifilesink->file == NULL) {
1105 GST_INFO_OBJECT (multifilesink, "opening file %s", filename);
1107 gst_multi_file_sink_add_old_file (multifilesink, filename);
1109 multifilesink->cur_file_size = 0;
1114 gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
1119 fclose (multifilesink->file);
1120 multifilesink->file = NULL;
1123 filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
1124 gst_multi_file_sink_post_message (multifilesink, buffer, filename);
1128 multifilesink->index++;