Merge branch 'upstream/1.16' into tizen_gst_1.16.2
[platform/upstream/gstreamer.git] / plugins / elements / gstfilesink.c
index d4badbd..90e4fa8 100644 (file)
  *
  * 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},
   };
 
@@ -95,7 +103,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_file_sink_debug);
 #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
 
@@ -106,6 +114,9 @@ enum
   PROP_BUFFER_MODE,
   PROP_BUFFER_SIZE,
   PROP_APPEND,
+#ifdef TIZEN_FEATURE_FILESINK_MODIFICATION
+  PROP_CURRENT_BYTES,
+#endif
   PROP_LAST
 };
 
@@ -162,6 +173,8 @@ static gboolean gst_file_sink_stop (GstBaseSink * sink);
 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);
@@ -173,6 +186,8 @@ static gboolean gst_file_sink_query (GstBaseSink * bsink, GstQuery * query);
 static void gst_file_sink_uri_handler_init (gpointer g_iface,
     gpointer iface_data);
 
+static GstFlowReturn gst_file_sink_flush_buffer (GstFileSink * filesink);
+
 #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");
@@ -199,7 +214,7 @@ gst_file_sink_class_init (GstFileSinkClass * klass)
 
   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,
@@ -207,30 +222,36 @@ gst_file_sink_class_init (GstFileSinkClass * klass)
           "Size of buffer in number of bytes for line or full buffer-mode", 0,
           G_MAXUINT, DEFAULT_BUFFER_SIZE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#ifdef TIZEN_FEATURE_FILESINK_MODIFICATION
+  g_object_class_install_property (gobject_class, PROP_CURRENT_BYTES,
+      g_param_spec_uint64 ("current-bytes", "Current bytes",
+          "downloaded bytes so far", 0,
+          G_MAXUINT64, 0,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
 
   /**
    * 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) {
@@ -244,9 +265,9 @@ 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);
@@ -263,9 +284,6 @@ gst_file_sink_dispose (GObject * object)
   sink->uri = NULL;
   g_free (sink->filename);
   sink->filename = NULL;
-  g_free (sink->buffer);
-  sink->buffer = NULL;
-  sink->buffer_size = 0;
 }
 
 static gboolean
@@ -282,8 +300,8 @@ gst_file_sink_set_location (GstFileSink * sink, const gchar * location,
      * 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;
@@ -347,6 +365,11 @@ gst_file_sink_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_APPEND:
       g_value_set_boolean (value, sink->append);
       break;
+#ifdef TIZEN_FEATURE_FILESINK_MODIFICATION
+    case PROP_CURRENT_BYTES:
+      g_value_set_uint64(value, sink->current_pos);
+      break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -356,8 +379,6 @@ gst_file_sink_get_property (GObject * object, guint prop_id, GValue * 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;
@@ -369,39 +390,23 @@ gst_file_sink_open_file (GstFileSink * sink)
   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);
 
@@ -427,24 +432,23 @@ static void
 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
@@ -463,7 +467,8 @@ gst_file_sink_query (GstBaseSink * bsink, GstQuery * query)
       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:
@@ -482,6 +487,16 @@ gst_file_sink_query (GstBaseSink * bsink, GstQuery * query)
       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;
@@ -503,8 +518,8 @@ gst_file_sink_do_seek (GstFileSink * filesink, guint64 new_offset)
   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)
@@ -525,9 +540,9 @@ gst_file_sink_do_seek (GstFileSink * filesink, guint64 new_offset)
   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:
@@ -558,7 +573,8 @@ gst_file_sink_event (GstBaseSink * sink, GstEvent * event)
       if (segment->format == GST_FORMAT_BYTES) {
         /* only try to seek and fail when we are going to a different
          * position */
-        if (filesink->current_pos != segment->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
@@ -575,9 +591,21 @@ gst_file_sink_event (GstBaseSink * sink, GstEvent * event)
       }
       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;
@@ -594,7 +622,14 @@ seek_failed:
     gst_event_unref (event);
     return FALSE;
   }
-flush_failed:
+flush_buffer_failed:
+  {
+    GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
+        (_("Error while writing to file \"%s\"."), filesink->filename), NULL);
+    gst_event_unref (event);
+    return FALSE;
+  }
+truncate_failed:
   {
     GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
         (_("Error while writing to file \"%s\"."), filesink->filename),
@@ -609,13 +644,14 @@ gst_file_sink_get_current_offset (GstFileSink * filesink, guint64 * p_pos)
 {
   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);
@@ -628,45 +664,209 @@ gst_file_sink_get_current_offset (GstFileSink * filesink, guint64 * p_pos)
 }
 
 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;
-  GstMapInfo info;
+  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);
+}
+
+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;
 
-  gst_buffer_map (buffer, &info, GST_MAP_READ);
+  *size += gst_buffer_get_size (*buffer);
 
-  GST_DEBUG_OBJECT (filesink,
-      "writing %" G_GSIZE_FORMAT " bytes at %" G_GUINT64_FORMAT,
-      info.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;
 
-  if (info.size > 0 && info.data != NULL) {
-    if (fwrite (info.data, info.size, 1, filesink->file) != 1)
-      goto handle_error;
+  sink = GST_FILE_SINK_CAST (bsink);
 
-    filesink->current_pos += info.size;
+  num_buffers = gst_buffer_list_length (buffer_list);
+  if (num_buffers == 0)
+    goto no_data;
+
+  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;
+  }
+
+  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;
+    }
   }
-  gst_buffer_unmap (buffer, &info);
 
-  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;
+  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;
     }
-    gst_buffer_unmap (buffer, &info);
-    return GST_FLOW_ERROR;
   }
+
+  return flow;
 }
 
 static gboolean