don't mix tabs and spaces
[platform/upstream/gstreamer.git] / gst / elements / gstfilesink.c
index c82ee1e..f5b5eac 100644 (file)
 #  include "config.h"
 #endif
 
+#include "../gst-i18n-lib.h"
+
 #include <gst/gst.h>
 #include <errno.h>
 #include "gstfilesink.h"
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 
-GST_DEBUG_CATEGORY (gst_filesink_debug);
+GST_DEBUG_CATEGORY_STATIC (gst_filesink_debug);
 #define GST_CAT_DEFAULT gst_filesink_debug
 
-GstElementDetails gst_filesink_details = {
-  "File Sink",
-  "Sink/File",
-  "LGPL",
-  "Write stream to a file",
-  VERSION,
-  "Thomas <thomas@apestaart.org>",
-  "(C) 2001"
-};
+GstElementDetails gst_filesink_details = GST_ELEMENT_DETAILS ("File Sink",
+    "Sink/File",
+    "Write stream to a file",
+    "Thomas <thomas@apestaart.org>");
 
 
 /* FileSink signals and args */
-enum {
+enum
+{
   /* FILL ME */
   SIGNAL_HANDOFF,
   LAST_SIGNAL
 };
 
-enum {
+enum
+{
   ARG_0,
   ARG_LOCATION
 };
 
-GST_PAD_EVENT_MASK_FUNCTION (gst_filesink_get_event_mask,
-  { GST_EVENT_SEEK, GST_SEEK_METHOD_CUR |
-                    GST_SEEK_METHOD_SET |
-                    GST_SEEK_METHOD_END |
-                    GST_SEEK_FLAG_FLUSH },
-  { GST_EVENT_FLUSH, 0 },
-  { GST_EVENT_DISCONTINUOUS, 0 }
-)
+static const GstFormat *
+gst_filesink_get_formats (GstPad * pad)
+{
+  static const GstFormat formats[] = {
+    GST_FORMAT_BYTES,
+    0,
+  };
+
+  return formats;
+}
 
-GST_PAD_QUERY_TYPE_FUNCTION (gst_filesink_get_query_types,
-  GST_QUERY_TOTAL,
-  GST_QUERY_POSITION
-)
+static const GstQueryType *
+gst_filesink_get_query_types (GstPad * pad)
+{
+  static const GstQueryType types[] = {
+    GST_QUERY_TOTAL,
+    GST_QUERY_POSITION,
+    0
+  };
 
-GST_PAD_FORMATS_FUNCTION (gst_filesink_get_formats,
-  GST_FORMAT_BYTES
-)
+  return types;
+}
 
+static void gst_filesink_dispose (GObject * object);
 
-static void    gst_filesink_class_init         (GstFileSinkClass *klass);
-static void    gst_filesink_init               (GstFileSink *filesink);
+static void gst_filesink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_filesink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
 
-static void    gst_filesink_set_property       (GObject *object, guint prop_id, 
-                                                const GValue *value, GParamSpec *pspec);
-static void    gst_filesink_get_property       (GObject *object, guint prop_id, 
-                                                GValue *value, GParamSpec *pspec);
+static gboolean gst_filesink_open_file (GstFileSink * sink);
+static void gst_filesink_close_file (GstFileSink * sink);
 
-static gboolean gst_filesink_open_file                 (GstFileSink *sink);
-static void    gst_filesink_close_file         (GstFileSink *sink);
+static gboolean gst_filesink_handle_event (GstPad * pad, GstEvent * event);
+static gboolean gst_filesink_pad_query (GstPad * pad, GstQueryType type,
+    GstFormat * format, gint64 * value);
+static void gst_filesink_chain (GstPad * pad, GstData * _data);
 
-static gboolean gst_filesink_handle_event       (GstPad *pad, GstEvent *event);
-static gboolean        gst_filesink_pad_query          (GstPad *pad, GstQueryType type,
-                                                GstFormat *format, gint64 *value);
-static void    gst_filesink_chain              (GstPad *pad,GstBuffer *buf);
+static void gst_filesink_uri_handler_init (gpointer g_iface,
+    gpointer iface_data);
 
-static GstElementStateReturn gst_filesink_change_state (GstElement *element);
+static GstElementStateReturn gst_filesink_change_state (GstElement * element);
 
-static GstElementClass *parent_class = NULL;
 static guint gst_filesink_signals[LAST_SIGNAL] = { 0 };
 
-GType
-gst_filesink_get_type (void) 
+static void
+_do_init (GType filesink_type)
 {
-  static GType filesink_type = 0;
-
-  if (!filesink_type) {
-    static const GTypeInfo filesink_info = {
-      sizeof(GstFileSinkClass),      NULL,
-      NULL,
-      (GClassInitFunc)gst_filesink_class_init,
-      NULL,
-      NULL,
-      sizeof(GstFileSink),
-      0,
-      (GInstanceInitFunc)gst_filesink_init,
-    };
-    filesink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstFileSink", &filesink_info, 0);
-  }
-  return filesink_type;
+  static const GInterfaceInfo urihandler_info = {
+    gst_filesink_uri_handler_init,
+    NULL,
+    NULL
+  };
+
+  g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER,
+      &urihandler_info);
+  GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "filesink", 0,
+      "filesink element");
 }
 
+GST_BOILERPLATE_FULL (GstFileSink, gst_filesink, GstElement, GST_TYPE_ELEMENT,
+    _do_init);
+
+
 static void
-gst_filesink_class_init (GstFileSinkClass *klass) 
+gst_filesink_base_init (gpointer g_class)
 {
-  GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
+  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
 
-  gobject_class = (GObjectClass*)klass;
-  gstelement_class = (GstElementClass*)klass;
+  gstelement_class->change_state = gst_filesink_change_state;
+  gst_element_class_set_details (gstelement_class, &gst_filesink_details);
+}
+static void
+gst_filesink_class_init (GstFileSinkClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
 
   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
-    g_param_spec_string ("location", "File Location", "Location of the file to write",
-                         NULL, G_PARAM_READWRITE));
+      g_param_spec_string ("location", "File Location",
+          "Location of the file to write", NULL, G_PARAM_READWRITE));
 
   gst_filesink_signals[SIGNAL_HANDOFF] =
-    g_signal_new ("handoff", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
-                    G_STRUCT_OFFSET (GstFileSinkClass, handoff), NULL, NULL,
-                    g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+      g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstFileSinkClass, handoff), NULL, NULL,
+      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
 
   gobject_class->set_property = gst_filesink_set_property;
   gobject_class->get_property = gst_filesink_get_property;
-
-  gstelement_class->change_state = gst_filesink_change_state;
+  gobject_class->dispose = gst_filesink_dispose;
 }
-
-static void 
-gst_filesink_init (GstFileSink *filesink) 
+static void
+gst_filesink_init (GstFileSink * filesink)
 {
   GstPad *pad;
 
@@ -152,9 +159,7 @@ gst_filesink_init (GstFileSink *filesink)
   gst_element_add_pad (GST_ELEMENT (filesink), pad);
   gst_pad_set_chain_function (pad, gst_filesink_chain);
 
-  GST_FLAG_SET (GST_ELEMENT(filesink), GST_ELEMENT_EVENT_AWARE);
-  gst_pad_set_event_function(pad, gst_filesink_handle_event);
-  gst_pad_set_event_mask_function(pad, gst_filesink_get_event_mask);
+  GST_FLAG_SET (GST_ELEMENT (filesink), GST_ELEMENT_EVENT_AWARE);
 
   gst_pad_set_query_function (pad, gst_filesink_pad_query);
   gst_pad_set_query_type_function (pad, gst_filesink_get_query_types);
@@ -163,9 +168,47 @@ gst_filesink_init (GstFileSink *filesink)
   filesink->filename = NULL;
   filesink->file = NULL;
 }
+static void
+gst_filesink_dispose (GObject * object)
+{
+  GstFileSink *sink = GST_FILESINK (object);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+
+  g_free (sink->uri);
+  sink->uri = NULL;
+  g_free (sink->filename);
+  sink->filename = NULL;
+}
 
+static gboolean
+gst_filesink_set_location (GstFileSink * sink, const gchar * location)
+{
+  /* the element must be stopped or paused in order to do this */
+  if (GST_STATE (sink) > GST_STATE_PAUSED)
+    return FALSE;
+  if (GST_STATE (sink) == GST_STATE_PAUSED &&
+      GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN))
+    return FALSE;
+
+  g_free (sink->filename);
+  g_free (sink->uri);
+  if (location != NULL) {
+    sink->filename = g_strdup (location);
+    sink->uri = gst_uri_construct ("file", location);
+  } else {
+    sink->filename = NULL;
+    sink->uri = NULL;
+  }
+
+  if (GST_STATE (sink) == GST_STATE_PAUSED)
+    gst_filesink_open_file (sink);
+
+  return TRUE;
+}
 static void
-gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+gst_filesink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
 {
   GstFileSink *sink;
 
@@ -174,16 +217,7 @@ gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value,
 
   switch (prop_id) {
     case ARG_LOCATION:
-      /* the element must be stopped or paused in order to do this */
-      g_return_if_fail (GST_STATE (sink) <= GST_STATE_PAUSED);
-      if (GST_STATE (sink) == GST_STATE_PAUSED)
-        g_return_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN));
-
-      if (sink->filename)
-       g_free (sink->filename);
-      sink->filename = g_strdup (g_value_get_string (value));
-      if (GST_STATE (sink) == GST_STATE_PAUSED)
-        gst_filesink_open_file (sink);   
+      gst_filesink_set_location (sink, g_value_get_string (value));
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -191,16 +225,17 @@ gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value,
   }
 }
 
-static void   
-gst_filesink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+static void
+gst_filesink_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
 {
   GstFileSink *sink;
+
   /* it's not null if we got it, but it might not be ours */
   g_return_if_fail (GST_IS_FILESINK (object));
+
   sink = GST_FILESINK (object);
-  
+
   switch (prop_id) {
     case ARG_LOCATION:
       g_value_set_string (value, sink->filename);
@@ -212,24 +247,24 @@ gst_filesink_get_property (GObject *object, guint prop_id, GValue *value, GParam
 }
 
 static gboolean
-gst_filesink_open_file (GstFileSink *sink)
+gst_filesink_open_file (GstFileSink * sink)
 {
   g_return_val_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN), FALSE);
 
   /* open the file */
-  if (!sink->filename)
-  {
-    /* Out of files */
+  if (sink->filename == NULL || sink->filename[0] == '\0') {
+    GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
+        (_("No file name specified for writing.")), (NULL));
     return FALSE;
   }
 
   sink->file = fopen (sink->filename, "w");
   if (sink->file == NULL) {
-    gst_element_error (GST_ELEMENT (sink),
-                      "Error opening file %s: %s",
-                      sink->filename, g_strerror(errno));
+    GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
+        (_("Could not open file \"%s\" for writing."), sink->filename),
+        GST_ERROR_SYSTEM);
     return FALSE;
-  } 
+  }
 
   GST_FLAG_SET (sink, GST_FILESINK_OPEN);
 
@@ -239,49 +274,46 @@ gst_filesink_open_file (GstFileSink *sink)
 }
 
 static void
-gst_filesink_close_file (GstFileSink *sink)
+gst_filesink_close_file (GstFileSink * sink)
 {
   g_return_if_fail (GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN));
 
-  if (fclose (sink->file) != 0)
-  {
-    gst_element_error (GST_ELEMENT (sink),
-                      "Error closing file %s: %s",
-                      sink->filename, g_strerror(errno));
-  }
-  else {
+  if (fclose (sink->file) != 0) {
+    GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
+        (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
+  } else {
     GST_FLAG_UNSET (sink, GST_FILESINK_OPEN);
   }
 }
 
 static gboolean
-gst_filesink_pad_query (GstPad *pad, GstQueryType type,
-                       GstFormat *format, gint64 *value)
+gst_filesink_pad_query (GstPad * pad, GstQueryType type,
+    GstFormat * format, gint64 * value)
 {
   GstFileSink *sink = GST_FILESINK (GST_PAD_PARENT (pad));
 
   switch (type) {
     case GST_QUERY_TOTAL:
       switch (*format) {
-       case GST_FORMAT_BYTES:
-          if (GST_FLAG_IS_SET (GST_ELEMENT(sink), GST_FILESINK_OPEN)) {
-            *value = sink->data_written; /* FIXME - doesn't the kernel provide
-                                           such a function? */
+        case GST_FORMAT_BYTES:
+          if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) {
+            *value = sink->data_written;        /* FIXME - doesn't the kernel provide
+                                                   such a function? */
             break;
           }
         default:
-         return FALSE;
+          return FALSE;
       }
       break;
     case GST_QUERY_POSITION:
       switch (*format) {
-       case GST_FORMAT_BYTES:
-          if (GST_FLAG_IS_SET (GST_ELEMENT(sink), GST_FILESINK_OPEN)) {
+        case GST_FORMAT_BYTES:
+          if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) {
             *value = ftell (sink->file);
             break;
           }
         default:
-         return FALSE;
+          return FALSE;
       }
       break;
     default:
@@ -293,31 +325,29 @@ gst_filesink_pad_query (GstPad *pad, GstQueryType type,
 
 /* handle events (search) */
 static gboolean
-gst_filesink_handle_event (GstPad *pad, GstEvent *event)
+gst_filesink_handle_event (GstPad * pad, GstEvent * event)
 {
   GstEventType type;
   GstFileSink *filesink;
 
   filesink = GST_FILESINK (gst_pad_get_parent (pad));
 
-  g_return_val_if_fail (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN),
-                       FALSE);
+  g_return_val_if_fail (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN), FALSE);
 
   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
 
   switch (type) {
     case GST_EVENT_SEEK:
       g_return_val_if_fail (GST_EVENT_SEEK_FORMAT (event) == GST_FORMAT_BYTES,
-                           FALSE);
+          FALSE);
 
       if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH)
         if (fflush (filesink->file))
-          gst_element_error (GST_ELEMENT (filesink),
-                            "Error flushing file %s: %s",
-                            filesink->filename, g_strerror(errno));
+          GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
+              (_("Error while writing to file \"%s\"."), filesink->filename),
+              GST_ERROR_SYSTEM);
 
-      switch (GST_EVENT_SEEK_METHOD(event))
-      {
+      switch (GST_EVENT_SEEK_METHOD (event)) {
         case GST_SEEK_METHOD_SET:
           fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_SET);
           break;
@@ -328,14 +358,14 @@ gst_filesink_handle_event (GstPad *pad, GstEvent *event)
           fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_END);
           break;
         default:
-          g_warning("unkown seek method!\n");
+          g_warning ("unknown seek method!");
           break;
       }
       break;
     case GST_EVENT_DISCONTINUOUS:
     {
       gint64 offset;
-      
+
       if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &offset))
         fseek (filesink->file, offset, SEEK_SET);
 
@@ -344,9 +374,9 @@ gst_filesink_handle_event (GstPad *pad, GstEvent *event)
     }
     case GST_EVENT_FLUSH:
       if (fflush (filesink->file)) {
-        gst_element_error (GST_ELEMENT (filesink),
-                          "Error flushing file %s: %s",
-                          filesink->filename, g_strerror(errno));
+        GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
+            (_("Error while writing to file \"%s\"."), filesink->filename),
+            GST_ERROR_SYSTEM);
       }
       break;
     case GST_EVENT_EOS:
@@ -368,9 +398,10 @@ gst_filesink_handle_event (GstPad *pad, GstEvent *event)
  *
  * take the buffer from the pad and write to file if it's open
  */
-static void 
-gst_filesink_chain (GstPad *pad, GstBuffer *buf) 
+static void
+gst_filesink_chain (GstPad * pad, GstData * _data)
 {
+  GstBuffer *buf = GST_BUFFER (_data);
   GstFileSink *filesink;
 
   g_return_if_fail (pad != NULL);
@@ -379,41 +410,42 @@ gst_filesink_chain (GstPad *pad, GstBuffer *buf)
 
   filesink = GST_FILESINK (gst_pad_get_parent (pad));
 
-  if (GST_IS_EVENT(buf))
-  {
-    gst_filesink_handle_event(pad, GST_EVENT(buf));
+  if (GST_IS_EVENT (buf)) {
+    gst_filesink_handle_event (pad, GST_EVENT (buf));
     return;
   }
 
-  if (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN))
-  {
-    guint bytes_written = 0;
-    do {
+  if (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN)) {
+    guint bytes_written = 0, back_pending = 0;
+
+    if (ftell (filesink->file) < filesink->data_written)
+      back_pending = filesink->data_written - ftell (filesink->file);
+    while (bytes_written < GST_BUFFER_SIZE (buf)) {
       size_t wrote = fwrite (GST_BUFFER_DATA (buf) + bytes_written, 1,
-                            GST_BUFFER_SIZE (buf) - bytes_written,
-                            filesink->file);
+          GST_BUFFER_SIZE (buf) - bytes_written,
+          filesink->file);
+
       if (wrote <= 0) {
-       gst_element_error (GST_ELEMENT (filesink),
-                          "Only %d of %d bytes written: %s",
-                          bytes_written, GST_BUFFER_SIZE (buf),
-                          strerror (errno));
-       break;
+        GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
+            (_("Error while writing to file \"%s\"."), filesink->filename),
+            ("Only %d of %d bytes written: %s",
+                bytes_written, GST_BUFFER_SIZE (buf), strerror (errno)));
+        break;
       }
       bytes_written += wrote;
-    } while (bytes_written < GST_BUFFER_SIZE (buf));
+    }
 
-    filesink->data_written += bytes_written;
+    filesink->data_written += bytes_written - back_pending;
   }
 
   gst_buffer_unref (buf);
 
   g_signal_emit (G_OBJECT (filesink),
-                 gst_filesink_signals[SIGNAL_HANDOFF], 0,
-                filesink);
+      gst_filesink_signals[SIGNAL_HANDOFF], 0, filesink);
 }
 
 static GstElementStateReturn
-gst_filesink_change_state (GstElement *element)
+gst_filesink_change_state (GstElement * element)
 {
   g_return_val_if_fail (GST_IS_FILESINK (element), GST_STATE_FAILURE);
 
@@ -436,3 +468,56 @@ gst_filesink_change_state (GstElement *element)
 
   return GST_STATE_SUCCESS;
 }
+
+/*** GSTURIHANDLER INTERFACE *************************************************/
+
+static guint
+gst_filesink_uri_get_type (void)
+{
+  return GST_URI_SINK;
+}
+static gchar **
+gst_filesink_uri_get_protocols (void)
+{
+  static gchar *protocols[] = { "file", NULL };
+
+  return protocols;
+}
+static const gchar *
+gst_filesink_uri_get_uri (GstURIHandler * handler)
+{
+  GstFileSink *sink = GST_FILESINK (handler);
+
+  return sink->uri;
+}
+
+static gboolean
+gst_filesink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+{
+  gchar *protocol, *location;
+  gboolean ret;
+  GstFileSink *sink = GST_FILESINK (handler);
+
+  protocol = gst_uri_get_protocol (uri);
+  if (strcmp (protocol, "file") != 0) {
+    g_free (protocol);
+    return FALSE;
+  }
+  g_free (protocol);
+  location = gst_uri_get_location (uri);
+  ret = gst_filesink_set_location (sink, location);
+  g_free (location);
+
+  return ret;
+}
+
+static void
+gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data)
+{
+  GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+  iface->get_type = gst_filesink_uri_get_type;
+  iface->get_protocols = gst_filesink_uri_get_protocols;
+  iface->get_uri = gst_filesink_uri_get_uri;
+  iface->set_uri = gst_filesink_uri_set_uri;
+}