*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-filesink
+ * @title: filesink
* @see_also: #GstFileSrc
*
* Write incoming data to a file in the local file system.
*
- * <refsect2>
- * <title>Example launch line</title>
+ * ## Example launch line
* |[
- * gst-launch v4l2src num-buffers=1 ! jpegenc ! filesink location=capture1.jpeg
+ * gst-launch-1.0 v4l2src num-buffers=1 ! jpegenc ! filesink location=capture1.jpeg
* ]| Capture one frame from a v4l2 camera and save as jpeg image.
- * </refsect2>
+ *
*/
#ifdef HAVE_CONFIG_H
#define lseek _lseeki64
#undef off_t
#define off_t guint64
+#undef ftruncate
+#define ftruncate _chsize
+#undef fsync
+#define fsync _commit
#ifdef _MSC_VER /* Check if we are using MSVC, fileno is deprecated in favour */
#define fileno _fileno /* of _fileno */
#endif
#include <unistd.h>
#endif
+#ifdef __BIONIC__ /* Android */
+#undef lseek
+#define lseek lseek64
+#undef off_t
+#define off_t guint64
+#endif
+
+#include "gstelements_private.h"
+#include "gstfilesink.h"
+
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
-#define GST_TYPE_BUFFER_MODE (buffer_mode_get_type ())
+#define GST_TYPE_FILE_SINK_BUFFER_MODE (gst_file_sink_buffer_mode_get_type ())
static GType
-buffer_mode_get_type (void)
+gst_file_sink_buffer_mode_get_type (void)
{
static GType buffer_mode_type = 0;
static const GEnumValue buffer_mode[] = {
- {-1, "Default buffering", "default"},
- {_IOFBF, "Fully buffered", "full"},
- {_IOLBF, "Line buffered", "line"},
- {_IONBF, "Unbuffered", "unbuffered"},
+ {GST_FILE_SINK_BUFFER_MODE_DEFAULT, "Default buffering", "default"},
+ {GST_FILE_SINK_BUFFER_MODE_FULL, "Fully buffered", "full"},
+ {GST_FILE_SINK_BUFFER_MODE_LINE, "Line buffered", "line"},
+ {GST_FILE_SINK_BUFFER_MODE_UNBUFFERED, "Unbuffered", "unbuffered"},
{0, NULL, NULL},
};
#define GST_CAT_DEFAULT gst_file_sink_debug
#define DEFAULT_LOCATION NULL
-#define DEFAULT_BUFFER_MODE -1
+#define DEFAULT_BUFFER_MODE GST_FILE_SINK_BUFFER_MODE_DEFAULT
#define DEFAULT_BUFFER_SIZE 64 * 1024
#define DEFAULT_APPEND FALSE
static gboolean gst_file_sink_event (GstBaseSink * sink, GstEvent * event);
static GstFlowReturn gst_file_sink_render (GstBaseSink * sink,
GstBuffer * buffer);
+static GstFlowReturn gst_file_sink_render_list (GstBaseSink * sink,
+ GstBufferList * list);
static gboolean gst_file_sink_do_seek (GstFileSink * filesink,
guint64 new_offset);
g_object_class_install_property (gobject_class, PROP_BUFFER_MODE,
g_param_spec_enum ("buffer-mode", "Buffering mode",
- "The buffering mode to use", GST_TYPE_BUFFER_MODE,
+ "The buffering mode to use", GST_TYPE_FILE_SINK_BUFFER_MODE,
DEFAULT_BUFFER_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
/**
* GstFileSink:append
- *
- * Append to an already existing file.
*
- * Since: 0.10.25
+ * Append to an already existing file.
*/
g_object_class_install_property (gobject_class, PROP_APPEND,
g_param_spec_boolean ("append", "Append",
"Append to an already existing file", DEFAULT_APPEND,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- gst_element_class_set_details_simple (gstelement_class,
+ gst_element_class_set_static_metadata (gstelement_class,
"File Sink",
"Sink/File", "Write stream to a file",
"Thomas Vander Stichele <thomas at apestaart dot org>");
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&sinktemplate));
+ gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_file_sink_start);
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_file_sink_stop);
gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_file_sink_query);
gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_file_sink_render);
+ gstbasesink_class->render_list =
+ GST_DEBUG_FUNCPTR (gst_file_sink_render_list);
gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_file_sink_event);
if (sizeof (off_t) < 8) {
{
filesink->filename = NULL;
filesink->file = NULL;
+ filesink->current_pos = 0;
filesink->buffer_mode = DEFAULT_BUFFER_MODE;
filesink->buffer_size = DEFAULT_BUFFER_SIZE;
filesink->buffer = NULL;
* this should be in UTF8 */
sink->filename = g_strdup (location);
sink->uri = gst_filename_to_uri (location, NULL);
- GST_INFO ("filename : %s", sink->filename);
- GST_INFO ("uri : %s", sink->uri);
+ GST_INFO_OBJECT (sink, "filename : %s", sink->filename);
+ GST_INFO_OBJECT (sink, "uri : %s", sink->uri);
} else {
sink->filename = NULL;
sink->uri = NULL;
sink->buffer = g_malloc (sink->buffer_size);
buffer_size = sink->buffer_size;
}
-#ifdef HAVE_STDIO_EXT_H
+ /* Cygwin does not have __fbufsize */
+#if defined(HAVE_STDIO_EXT_H) && !defined(__CYGWIN__)
GST_DEBUG_OBJECT (sink, "change buffer size %u to %u, mode %d",
(guint) __fbufsize (sink->file), buffer_size, mode);
#else
{
if (sink->file) {
if (fclose (sink->file) != 0)
- goto close_failed;
+ GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
+ (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
GST_DEBUG_OBJECT (sink, "closed file");
sink->file = NULL;
g_free (sink->buffer);
sink->buffer = NULL;
}
- return;
-
- /* ERRORS */
-close_failed:
- {
- GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
- (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
- return;
- }
}
static gboolean
res = TRUE;
break;
+ case GST_QUERY_SEEKING:
+ gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
+ if (format == GST_FORMAT_BYTES || format == GST_FORMAT_DEFAULT) {
+ gst_query_set_seeking (query, GST_FORMAT_BYTES, self->seekable, 0, -1);
+ } else {
+ gst_query_set_seeking (query, format, FALSE, 0, -1);
+ }
+ res = TRUE;
+ break;
+
default:
res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
break;
}
break;
}
+ case GST_EVENT_FLUSH_STOP:
+ if (filesink->current_pos != 0 && filesink->seekable) {
+ gst_file_sink_do_seek (filesink, 0);
+ if (ftruncate (fileno (filesink->file), 0))
+ goto flush_failed;
+ }
+ break;
case GST_EVENT_EOS:
if (fflush (filesink->file))
goto flush_failed;
break;
}
- return TRUE;
+ return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
/* ERRORS */
seek_failed:
GST_ELEMENT_ERROR (filesink, RESOURCE, SEEK,
(_("Error while seeking in file \"%s\"."), filesink->filename),
GST_ERROR_SYSTEM);
+ gst_event_unref (event);
return FALSE;
}
flush_failed:
GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
(_("Error while writing to file \"%s\"."), filesink->filename),
GST_ERROR_SYSTEM);
+ gst_event_unref (event);
return FALSE;
}
}
}
static GstFlowReturn
-gst_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
+gst_file_sink_render_buffers (GstFileSink * sink, GstBuffer ** buffers,
+ guint num_buffers, guint8 * mem_nums, guint total_mems)
{
- GstFileSink *filesink;
- gsize size;
- guint8 *data;
-
- filesink = GST_FILE_SINK (sink);
+ GST_DEBUG_OBJECT (sink,
+ "writing %u buffers (%u memories) at position %" G_GUINT64_FORMAT,
+ num_buffers, total_mems, sink->current_pos);
- data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
+ return gst_writev_buffers (GST_OBJECT_CAST (sink), fileno (sink->file), NULL,
+ buffers, num_buffers, mem_nums, total_mems, &sink->current_pos, 0);
+}
- GST_DEBUG_OBJECT (filesink,
- "writing %" G_GSIZE_FORMAT " bytes at %" G_GUINT64_FORMAT,
- size, filesink->current_pos);
+static GstFlowReturn
+gst_file_sink_render_list (GstBaseSink * bsink, GstBufferList * buffer_list)
+{
+ GstFlowReturn flow;
+ GstBuffer **buffers;
+ GstFileSink *sink;
+ guint8 *mem_nums;
+ guint total_mems;
+ guint i, num_buffers;
+ gboolean sync_after = FALSE;
+
+ sink = GST_FILE_SINK_CAST (bsink);
+
+ num_buffers = gst_buffer_list_length (buffer_list);
+ if (num_buffers == 0)
+ goto no_data;
+
+ /* extract buffers from list and count memories */
+ buffers = g_newa (GstBuffer *, num_buffers);
+ mem_nums = g_newa (guint8, num_buffers);
+ for (i = 0, total_mems = 0; i < num_buffers; ++i) {
+ buffers[i] = gst_buffer_list_get (buffer_list, i);
+ mem_nums[i] = gst_buffer_n_memory (buffers[i]);
+ total_mems += mem_nums[i];
+ if (GST_BUFFER_FLAG_IS_SET (buffers[i], GST_BUFFER_FLAG_SYNC_AFTER))
+ sync_after = TRUE;
+ }
- if (size > 0 && data != NULL) {
- if (fwrite (data, size, 1, filesink->file) != 1)
- goto handle_error;
+ flow =
+ gst_file_sink_render_buffers (sink, buffers, num_buffers, mem_nums,
+ total_mems);
- filesink->current_pos += size;
+ if (flow == GST_FLOW_OK && sync_after) {
+ if (fflush (sink->file) || fsync (fileno (sink->file))) {
+ GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
+ (_("Error while writing to file \"%s\"."), sink->filename),
+ ("%s", g_strerror (errno)));
+ flow = GST_FLOW_ERROR;
+ }
}
- gst_buffer_unmap (buffer, data, size);
- return GST_FLOW_OK;
+ return flow;
-handle_error:
+no_data:
{
- switch (errno) {
- case ENOSPC:{
- GST_ELEMENT_ERROR (filesink, RESOURCE, NO_SPACE_LEFT, (NULL), (NULL));
- break;
- }
- default:{
- GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
- (_("Error while writing to file \"%s\"."), filesink->filename),
- ("%s", g_strerror (errno)));
- }
+ GST_LOG_OBJECT (sink, "empty buffer list");
+ return GST_FLOW_OK;
+ }
+}
+
+static GstFlowReturn
+gst_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
+{
+ GstFileSink *filesink;
+ GstFlowReturn flow;
+ guint8 n_mem;
+
+ filesink = GST_FILE_SINK_CAST (sink);
+
+ n_mem = gst_buffer_n_memory (buffer);
+
+ if (n_mem > 0)
+ flow = gst_file_sink_render_buffers (filesink, &buffer, 1, &n_mem, n_mem);
+ else
+ flow = GST_FLOW_OK;
+
+ if (flow == GST_FLOW_OK &&
+ GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_SYNC_AFTER)) {
+ if (fflush (filesink->file) || fsync (fileno (filesink->file))) {
+ GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
+ (_("Error while writing to file \"%s\"."), filesink->filename),
+ ("%s", g_strerror (errno)));
+ flow = GST_FLOW_ERROR;
}
- gst_buffer_unmap (buffer, data, size);
- return GST_FLOW_ERROR;
}
+
+ return flow;
}
static gboolean