*
* 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
+#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 (deprecated, like full)",
+ "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);
static void gst_file_sink_uri_handler_init (gpointer g_iface,
gpointer iface_data);
+static GstFlowReturn gst_file_sink_flush_buffer (GstFileSink * filesink);
-static void
-_do_init (GType filesink_type)
-{
- static const GInterfaceInfo urihandler_info = {
- gst_file_sink_uri_handler_init,
- NULL,
- NULL
- };
-
- g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER,
- &urihandler_info);
- GST_DEBUG_CATEGORY_INIT (gst_file_sink_debug, "filesink", 0,
- "filesink element");
-}
-
-GST_BOILERPLATE_FULL (GstFileSink, gst_file_sink, GstBaseSink,
- GST_TYPE_BASE_SINK, _do_init);
-
-static void
-gst_file_sink_base_init (gpointer g_class)
-{
- GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_set_details_simple (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));
-}
+#define _do_init \
+ G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_file_sink_uri_handler_init); \
+ GST_DEBUG_CATEGORY_INIT (gst_file_sink_debug, "filesink", 0, "filesink element");
+#define gst_file_sink_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstFileSink, gst_file_sink, GST_TYPE_BASE_SINK,
+ _do_init);
static void
gst_file_sink_class_init (GstFileSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
gobject_class->dispose = gst_file_sink_dispose;
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_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_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) {
}
static void
-gst_file_sink_init (GstFileSink * filesink, GstFileSinkClass * g_class)
+gst_file_sink_init (GstFileSink * filesink)
{
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;
filesink->append = FALSE;
gst_base_sink_set_sync (GST_BASE_SINK (filesink), FALSE);
sink->uri = NULL;
g_free (sink->filename);
sink->filename = NULL;
- g_free (sink->buffer);
- sink->buffer = NULL;
- sink->buffer_size = 0;
}
static gboolean
-gst_file_sink_set_location (GstFileSink * sink, const gchar * location)
+gst_file_sink_set_location (GstFileSink * sink, const gchar * location,
+ GError ** error)
{
if (sink->file)
goto was_open;
* 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;
{
g_warning ("Changing the `location' property on filesink when a file is "
"open is not supported.");
+ g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
+ "Changing the 'location' property on filesink when a file is "
+ "open is not supported");
return FALSE;
}
}
switch (prop_id) {
case PROP_LOCATION:
- gst_file_sink_set_location (sink, g_value_get_string (value));
+ gst_file_sink_set_location (sink, g_value_get_string (value), NULL);
break;
case PROP_BUFFER_MODE:
sink->buffer_mode = g_value_get_enum (value);
static gboolean
gst_file_sink_open_file (GstFileSink * sink)
{
- gint mode;
-
/* open the file */
if (sink->filename == NULL || sink->filename[0] == '\0')
goto no_filename;
if (sink->file == NULL)
goto open_failed;
- /* see if we are asked to perform a specific kind of buffering */
- if ((mode = sink->buffer_mode) != -1) {
- guint buffer_size;
-
- /* free previous buffer if any */
- g_free (sink->buffer);
-
- if (mode == _IONBF) {
- /* no buffering */
- sink->buffer = NULL;
- buffer_size = 0;
- } else {
- /* allocate buffer */
- sink->buffer = g_malloc (sink->buffer_size);
- buffer_size = sink->buffer_size;
- }
-#ifdef HAVE_STDIO_EXT_H
- GST_DEBUG_OBJECT (sink, "change buffer size %u to %u, mode %d",
- (guint) __fbufsize (sink->file), buffer_size, mode);
-#else
- GST_DEBUG_OBJECT (sink, "change buffer size to %u, mode %d",
- sink->buffer_size, mode);
-#endif
- if (setvbuf (sink->file, sink->buffer, mode, buffer_size) != 0) {
- GST_WARNING_OBJECT (sink, "warning: setvbuf failed: %s",
- g_strerror (errno));
- }
- }
-
sink->current_pos = 0;
/* try to seek in the file to figure out if it is seekable */
sink->seekable = gst_file_sink_do_seek (sink, 0);
+ if (sink->buffer)
+ gst_buffer_list_unref (sink->buffer);
+ sink->buffer = NULL;
+ if (sink->buffer_mode != GST_FILE_SINK_BUFFER_MODE_UNBUFFERED) {
+ if (sink->buffer_size == 0) {
+ sink->buffer_size = DEFAULT_BUFFER_SIZE;
+ g_object_notify (G_OBJECT (sink), "buffer-size");
+ }
+
+ sink->buffer = gst_buffer_list_new ();
+ sink->current_buffer_size = 0;
+ }
+
GST_DEBUG_OBJECT (sink, "opened file %s, seekable %d",
sink->filename, sink->seekable);
gst_file_sink_close_file (GstFileSink * sink)
{
if (sink->file) {
+ if (gst_file_sink_flush_buffer (sink) != GST_FLOW_OK)
+ GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
+ (_("Error closing file \"%s\"."), sink->filename), NULL);
+
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;
+ if (sink->buffer) {
+ gst_buffer_list_unref (sink->buffer);
+ sink->buffer = NULL;
}
+ sink->current_buffer_size = 0;
}
static gboolean
switch (format) {
case GST_FORMAT_DEFAULT:
case GST_FORMAT_BYTES:
- gst_query_set_position (query, GST_FORMAT_BYTES, self->current_pos);
+ gst_query_set_position (query, GST_FORMAT_BYTES,
+ self->current_pos + self->current_buffer_size);
res = TRUE;
break;
default:
GST_DEBUG_OBJECT (filesink, "Seeking to offset %" G_GUINT64_FORMAT
" using " __GST_STDIO_SEEK_FUNCTION, new_offset);
- if (fflush (filesink->file))
- goto flush_failed;
+ if (gst_file_sink_flush_buffer (filesink) != GST_FLOW_OK)
+ goto flush_buffer_failed;
#ifdef HAVE_FSEEKO
if (fseeko (filesink->file, (off_t) new_offset, SEEK_SET) != 0)
return TRUE;
/* ERRORS */
-flush_failed:
+flush_buffer_failed:
{
- GST_DEBUG_OBJECT (filesink, "Flush failed: %s", g_strerror (errno));
+ GST_DEBUG_OBJECT (filesink, "Flushing buffer failed");
return FALSE;
}
seek_failed:
type = GST_EVENT_TYPE (event);
switch (type) {
- case GST_EVENT_NEWSEGMENT:
+ case GST_EVENT_SEGMENT:
{
- gint64 start, stop, pos;
- GstFormat format;
+ const GstSegment *segment;
- gst_event_parse_new_segment (event, NULL, NULL, &format, &start,
- &stop, &pos);
+ gst_event_parse_segment (event, &segment);
- if (format == GST_FORMAT_BYTES) {
+ if (segment->format == GST_FORMAT_BYTES) {
/* only try to seek and fail when we are going to a different
* position */
- if (filesink->current_pos != start) {
+ if (filesink->current_pos + filesink->current_buffer_size !=
+ segment->start) {
/* FIXME, the seek should be performed on the pos field, start/stop are
* just boundaries for valid bytes offsets. We should also fill the file
* with zeroes if the new position extends the current EOF (sparse streams
* and segment accumulation). */
- if (!gst_file_sink_do_seek (filesink, (guint64) start))
+ if (!gst_file_sink_do_seek (filesink, (guint64) segment->start))
goto seek_failed;
} else {
- GST_DEBUG_OBJECT (filesink, "Ignored NEWSEGMENT, no seek needed");
+ GST_DEBUG_OBJECT (filesink, "Ignored SEGMENT, no seek needed");
}
} else {
GST_DEBUG_OBJECT (filesink,
- "Ignored NEWSEGMENT event of format %u (%s)", (guint) format,
- gst_format_get_name (format));
+ "Ignored SEGMENT event of format %u (%s)", (guint) segment->format,
+ gst_format_get_name (segment->format));
}
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 truncate_failed;
+ }
+ if (filesink->buffer) {
+ gst_buffer_list_unref (filesink->buffer);
+ filesink->buffer = gst_buffer_list_new ();
+ filesink->current_buffer_size = 0;
+ }
+ break;
case GST_EVENT_EOS:
- if (fflush (filesink->file))
- goto flush_failed;
+ if (gst_file_sink_flush_buffer (filesink) != GST_FLOW_OK)
+ goto flush_buffer_failed;
break;
default:
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_buffer_failed:
+ {
+ GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
+ (_("Error while writing to file \"%s\"."), filesink->filename), NULL);
+ gst_event_unref (event);
return FALSE;
}
-flush_failed:
+truncate_failed:
{
GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
(_("Error while writing to file \"%s\"."), filesink->filename),
GST_ERROR_SYSTEM);
+ gst_event_unref (event);
return FALSE;
}
}
{
off_t ret = -1;
+ /* no need to flush internal buffer here as this is only called right
+ * after a seek. If this changes then the buffer should be flushed here
+ * too
+ */
+
#ifdef HAVE_FTELLO
ret = ftello (filesink->file);
#elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
- if (fflush (filesink->file)) {
- GST_DEBUG_OBJECT (filesink, "Flush failed: %s", g_strerror (errno));
- /* ignore and continue */
- }
ret = lseek (fileno (filesink->file), 0, SEEK_CUR);
#else
ret = (off_t) ftell (filesink->file);
}
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, gsize size)
{
- GstFileSink *filesink;
- guint size;
- guint8 *data;
+ GST_DEBUG_OBJECT (sink,
+ "writing %u buffers (%u memories, %" G_GSIZE_FORMAT
+ " bytes) at position %" G_GUINT64_FORMAT, num_buffers, total_mems, size,
+ sink->current_pos);
- filesink = GST_FILE_SINK (sink);
+ return gst_writev_buffers (GST_OBJECT_CAST (sink), fileno (sink->file), NULL,
+ buffers, num_buffers, mem_nums, total_mems, &sink->current_pos, 0);
+}
- size = GST_BUFFER_SIZE (buffer);
- data = GST_BUFFER_DATA (buffer);
+static GstFlowReturn
+gst_file_sink_render_list_internal (GstFileSink * sink,
+ GstBufferList * buffer_list)
+{
+ GstFlowReturn flow;
+ GstBuffer **buffers;
+ guint8 *mem_nums;
+ guint total_mems;
+ gsize total_size = 0;
+ guint i, num_buffers;
+
+ 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];
+ total_size += gst_buffer_get_size (buffers[i]);
+ }
+
+ flow =
+ gst_file_sink_render_buffers (sink, buffers, num_buffers, mem_nums,
+ total_mems, total_size);
+
+ return flow;
+
+no_data:
+ {
+ GST_LOG_OBJECT (sink, "empty buffer list");
+ return GST_FLOW_OK;
+ }
+}
+
+static GstFlowReturn
+gst_file_sink_flush_buffer (GstFileSink * filesink)
+{
+ GstFlowReturn flow_ret = GST_FLOW_OK;
+
+ if (filesink->buffer) {
+ guint length;
+
+ length = gst_buffer_list_length (filesink->buffer);
+
+ if (length > 0) {
+ GST_DEBUG_OBJECT (filesink, "Flushing out buffer of size %u",
+ filesink->current_buffer_size);
+ flow_ret =
+ gst_file_sink_render_list_internal (filesink, filesink->buffer);
+ /* Remove all buffers from the list but keep the list. This ensures that
+ * we don't re-allocate the array storing the buffers all the time */
+ gst_buffer_list_remove (filesink->buffer, 0, length);
+ filesink->current_buffer_size = 0;
+ }
+ }
+
+ return flow_ret;
+}
+
+static gboolean
+has_sync_after_buffer (GstBuffer ** buffer, guint idx, gpointer user_data)
+{
+ if (GST_BUFFER_FLAG_IS_SET (*buffer, GST_BUFFER_FLAG_SYNC_AFTER)) {
+ gboolean *sync_after = user_data;
+
+ *sync_after = TRUE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+accumulate_size (GstBuffer ** buffer, guint idx, gpointer user_data)
+{
+ guint *size = user_data;
+
+ *size += gst_buffer_get_size (*buffer);
- GST_DEBUG_OBJECT (filesink, "writing %u bytes at %" G_GUINT64_FORMAT,
- size, filesink->current_pos);
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_file_sink_render_list (GstBaseSink * bsink, GstBufferList * buffer_list)
+{
+ GstFlowReturn flow;
+ GstFileSink *sink;
+ guint i, num_buffers;
+ gboolean sync_after = FALSE;
+
+ sink = GST_FILE_SINK_CAST (bsink);
- if (size > 0 && data != NULL) {
- if (fwrite (data, size, 1, filesink->file) != 1)
- goto handle_error;
+ num_buffers = gst_buffer_list_length (buffer_list);
+ if (num_buffers == 0)
+ goto no_data;
- filesink->current_pos += size;
+ gst_buffer_list_foreach (buffer_list, has_sync_after_buffer, &sync_after);
+
+ if (sync_after || !sink->buffer) {
+ flow = gst_file_sink_flush_buffer (sink);
+ if (flow == GST_FLOW_OK)
+ flow = gst_file_sink_render_list_internal (sink, buffer_list);
+ } else {
+ guint size = 0;
+ gst_buffer_list_foreach (buffer_list, accumulate_size, &size);
+
+ GST_DEBUG_OBJECT (sink,
+ "Queueing buffer list of %u bytes (%u buffers) at offset %"
+ G_GUINT64_FORMAT, size, num_buffers,
+ sink->current_pos + sink->current_buffer_size);
+
+ for (i = 0; i < num_buffers; ++i)
+ gst_buffer_list_add (sink->buffer,
+ gst_buffer_ref (gst_buffer_list_get (buffer_list, i)));
+ sink->current_buffer_size += size;
+
+ if (sink->current_buffer_size > sink->buffer_size)
+ flow = gst_file_sink_flush_buffer (sink);
+ else
+ flow = GST_FLOW_OK;
}
- return GST_FLOW_OK;
+ if (flow == GST_FLOW_OK && sync_after) {
+ if (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;
+ }
+ }
-handle_error:
+ return flow;
+
+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;
+ gboolean sync_after;
+
+ filesink = GST_FILE_SINK_CAST (sink);
+
+ sync_after = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_SYNC_AFTER);
+
+ n_mem = gst_buffer_n_memory (buffer);
+
+ if (n_mem > 0 && (sync_after || !filesink->buffer)) {
+ flow = gst_file_sink_flush_buffer (filesink);
+ if (flow == GST_FLOW_OK)
+ flow =
+ gst_file_sink_render_buffers (filesink, &buffer, 1, &n_mem, n_mem,
+ gst_buffer_get_size (buffer));
+ } else if (n_mem > 0) {
+ GST_DEBUG_OBJECT (filesink,
+ "Queueing buffer of %" G_GSIZE_FORMAT " bytes at offset %"
+ G_GUINT64_FORMAT, gst_buffer_get_size (buffer),
+ filesink->current_pos + filesink->current_buffer_size);
+
+ filesink->current_buffer_size += gst_buffer_get_size (buffer);
+ gst_buffer_list_add (filesink->buffer, gst_buffer_ref (buffer));
+
+ if (filesink->current_buffer_size > filesink->buffer_size)
+ flow = gst_file_sink_flush_buffer (filesink);
+ else
+ flow = GST_FLOW_OK;
+ } else {
+ flow = GST_FLOW_OK;
+ }
+
+ if (flow == GST_FLOW_OK && sync_after) {
+ if (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;
}
- return GST_FLOW_ERROR;
}
+
+ return flow;
}
static gboolean
/*** GSTURIHANDLER INTERFACE *************************************************/
static GstURIType
-gst_file_sink_uri_get_type (void)
+gst_file_sink_uri_get_type (GType type)
{
return GST_URI_SINK;
}
-static gchar **
-gst_file_sink_uri_get_protocols (void)
+static const gchar *const *
+gst_file_sink_uri_get_protocols (GType type)
{
- static gchar *protocols[] = { (char *) "file", NULL };
+ static const gchar *protocols[] = { "file", NULL };
return protocols;
}
-static const gchar *
+static gchar *
gst_file_sink_uri_get_uri (GstURIHandler * handler)
{
GstFileSink *sink = GST_FILE_SINK (handler);
- return sink->uri;
+ /* FIXME: make thread-safe */
+ return g_strdup (sink->uri);
}
static gboolean
-gst_file_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+gst_file_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri,
+ GError ** error)
{
- gchar *protocol, *location;
+ gchar *location;
gboolean ret;
GstFileSink *sink = GST_FILE_SINK (handler);
- protocol = gst_uri_get_protocol (uri);
- if (strcmp (protocol, "file") != 0) {
- g_free (protocol);
- return FALSE;
- }
- g_free (protocol);
-
/* allow file://localhost/foo/bar by stripping localhost but fail
* for every other hostname */
if (g_str_has_prefix (uri, "file://localhost/")) {
/* Special case for "file://" as this is used by some applications
* to test with gst_element_make_from_uri if there's an element
* that supports the URI protocol. */
- gst_file_sink_set_location (sink, NULL);
+ gst_file_sink_set_location (sink, NULL, NULL);
return TRUE;
} else {
location = gst_uri_get_location (uri);
}
- if (!location)
+ if (!location) {
+ g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
+ "File URI without location");
return FALSE;
+ }
+
if (!g_path_is_absolute (location)) {
+ g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
+ "File URI location must be an absolute path");
g_free (location);
return FALSE;
}
- ret = gst_file_sink_set_location (sink, location);
+ ret = gst_file_sink_set_location (sink, location, error);
g_free (location);
return ret;