do CVS surgery and related build fixery to move elements and indexers in a new gstrea...
[platform/upstream/gstreamer.git] / plugins / elements / gstfilesink.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstfilesink.c:
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 /**
23  * SECTION:gstfilesink
24  * @short_description: write stream to a file
25  * @see_also: #GstFileSrc
26  *
27  * Write incoming data to a file in the local file system.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #  include "config.h"
32 #endif
33
34 #include "../../gst/gst-i18n-lib.h"
35
36 #include <gst/gst.h>
37 #include <errno.h>
38 #include "gstfilesink.h"
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46
47 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
48     GST_PAD_SINK,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS_ANY);
51
52 GST_DEBUG_CATEGORY_STATIC (gst_file_sink_debug);
53 #define GST_CAT_DEFAULT gst_file_sink_debug
54
55 static GstElementDetails gst_file_sink_details =
56 GST_ELEMENT_DETAILS ("File Sink",
57     "Sink/File",
58     "Write stream to a file",
59     "Thomas <thomas@apestaart.org>");
60
61 enum
62 {
63   ARG_0,
64   ARG_LOCATION
65 };
66
67 static void gst_file_sink_dispose (GObject * object);
68
69 static void gst_file_sink_set_property (GObject * object, guint prop_id,
70     const GValue * value, GParamSpec * pspec);
71 static void gst_file_sink_get_property (GObject * object, guint prop_id,
72     GValue * value, GParamSpec * pspec);
73
74 static gboolean gst_file_sink_open_file (GstFileSink * sink);
75 static void gst_file_sink_close_file (GstFileSink * sink);
76
77 static gboolean gst_file_sink_start (GstBaseSink * sink);
78 static gboolean gst_file_sink_stop (GstBaseSink * sink);
79 static gboolean gst_file_sink_event (GstBaseSink * sink, GstEvent * event);
80 static GstFlowReturn gst_file_sink_render (GstBaseSink * sink,
81     GstBuffer * buffer);
82
83 static gboolean gst_file_sink_query (GstPad * pad, GstQuery * query);
84
85 static void gst_file_sink_uri_handler_init (gpointer g_iface,
86     gpointer iface_data);
87
88
89 static void
90 _do_init (GType filesink_type)
91 {
92   static const GInterfaceInfo urihandler_info = {
93     gst_file_sink_uri_handler_init,
94     NULL,
95     NULL
96   };
97
98   g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER,
99       &urihandler_info);
100   GST_DEBUG_CATEGORY_INIT (gst_file_sink_debug, "filesink", 0,
101       "filesink element");
102 }
103
104 GST_BOILERPLATE_FULL (GstFileSink, gst_file_sink, GstBaseSink,
105     GST_TYPE_BASE_SINK, _do_init);
106
107 static void
108 gst_file_sink_base_init (gpointer g_class)
109 {
110   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
111
112   gst_element_class_add_pad_template (gstelement_class,
113       gst_static_pad_template_get (&sinktemplate));
114   gst_element_class_set_details (gstelement_class, &gst_file_sink_details);
115 }
116
117 static void
118 gst_file_sink_class_init (GstFileSinkClass * klass)
119 {
120   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
121   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
122
123   gobject_class->set_property = gst_file_sink_set_property;
124   gobject_class->get_property = gst_file_sink_get_property;
125
126   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
127       g_param_spec_string ("location", "File Location",
128           "Location of the file to write", NULL, G_PARAM_READWRITE));
129
130   gobject_class->dispose = gst_file_sink_dispose;
131
132   gstbasesink_class->get_times = NULL;
133   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_file_sink_start);
134   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_file_sink_stop);
135   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_file_sink_render);
136   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_file_sink_event);
137
138   if (sizeof (off_t) < 8) {
139     GST_LOG ("No large file support, sizeof (off_t) = %u", sizeof (off_t));
140   }
141 }
142
143 static void
144 gst_file_sink_init (GstFileSink * filesink, GstFileSinkClass * g_class)
145 {
146   GstPad *pad;
147
148   pad = GST_BASE_SINK_PAD (filesink);
149
150   gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_file_sink_query));
151
152   filesink->filename = NULL;
153   filesink->file = NULL;
154
155   GST_BASE_SINK (filesink)->sync = FALSE;
156 }
157
158 static void
159 gst_file_sink_dispose (GObject * object)
160 {
161   GstFileSink *sink = GST_FILE_SINK (object);
162
163   G_OBJECT_CLASS (parent_class)->dispose (object);
164
165   g_free (sink->uri);
166   sink->uri = NULL;
167   g_free (sink->filename);
168   sink->filename = NULL;
169 }
170
171 static gboolean
172 gst_file_sink_set_location (GstFileSink * sink, const gchar * location)
173 {
174   if (sink->file) {
175     g_warning ("Changing the `location' property on filesink when "
176         "a file is open not supported.");
177     return FALSE;
178   }
179
180   g_free (sink->filename);
181   g_free (sink->uri);
182   if (location != NULL) {
183     sink->filename = g_strdup (location);
184     sink->uri = gst_uri_construct ("file", location);
185   } else {
186     sink->filename = NULL;
187     sink->uri = NULL;
188   }
189
190   return TRUE;
191 }
192 static void
193 gst_file_sink_set_property (GObject * object, guint prop_id,
194     const GValue * value, GParamSpec * pspec)
195 {
196   GstFileSink *sink = GST_FILE_SINK (object);
197
198   switch (prop_id) {
199     case ARG_LOCATION:
200       gst_file_sink_set_location (sink, g_value_get_string (value));
201       break;
202     default:
203       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
204       break;
205   }
206 }
207
208 static void
209 gst_file_sink_get_property (GObject * object, guint prop_id, GValue * value,
210     GParamSpec * pspec)
211 {
212   GstFileSink *sink = GST_FILE_SINK (object);
213
214   switch (prop_id) {
215     case ARG_LOCATION:
216       g_value_set_string (value, sink->filename);
217       break;
218     default:
219       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
220       break;
221   }
222 }
223
224 static gboolean
225 gst_file_sink_open_file (GstFileSink * sink)
226 {
227   /* open the file */
228   if (sink->filename == NULL || sink->filename[0] == '\0') {
229     GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
230         (_("No file name specified for writing.")), (NULL));
231     return FALSE;
232   }
233
234   sink->file = fopen (sink->filename, "wb");
235   if (sink->file == NULL) {
236     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
237         (_("Could not open file \"%s\" for writing."), sink->filename),
238         GST_ERROR_SYSTEM);
239     return FALSE;
240   }
241
242   sink->data_written = 0;
243
244   return TRUE;
245 }
246
247 static void
248 gst_file_sink_close_file (GstFileSink * sink)
249 {
250   if (sink->file) {
251     if (fclose (sink->file) != 0) {
252       GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
253           (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
254     }
255   }
256 }
257
258 static gboolean
259 gst_file_sink_query (GstPad * pad, GstQuery * query)
260 {
261   GstFileSink *self;
262   GstFormat format;
263
264   self = GST_FILE_SINK (GST_PAD_PARENT (pad));
265
266   switch (GST_QUERY_TYPE (query)) {
267     case GST_QUERY_POSITION:
268       gst_query_parse_position (query, &format, NULL);
269       switch (format) {
270         case GST_FORMAT_DEFAULT:
271         case GST_FORMAT_BYTES:
272           gst_query_set_position (query, GST_FORMAT_BYTES, self->data_written);
273           return TRUE;
274         default:
275           return FALSE;
276       }
277
278     case GST_QUERY_FORMATS:
279       gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
280       return TRUE;
281
282     default:
283       return gst_pad_query_default (pad, query);
284   }
285 }
286
287 #if HAVE_FSEEKO
288 # define __GST_STDIO_SEEK_FUNCTION "fseeko"
289 #elif G_OS_UNIX
290 # define __GST_STDIO_SEEK_FUNCTION "lseek"
291 #else
292 # define __GST_STDIO_SEEK_FUNCTION "fseek"
293 #endif
294
295 static void
296 gst_file_sink_do_seek (GstFileSink * filesink, guint64 new_offset)
297 {
298   GST_DEBUG_OBJECT (filesink, "Seeking to offset %" G_GUINT64_FORMAT
299       " using " __GST_STDIO_SEEK_FUNCTION, new_offset);
300
301   if (fflush (filesink->file))
302     goto flush_failed;
303
304 #if HAVE_FSEEKO
305   if (fseeko (filesink->file, (off_t) new_offset, SEEK_SET) != 0)
306     goto seek_failed;
307 #elif G_OS_UNIX
308   if (lseek (fileno (filesink->file), (off_t) new_offset,
309           SEEK_SET) == (off_t) - 1)
310     goto seek_failed;
311 #else
312   if (fseek (filesink->file, (long) new_offset, SEEK_SET) != 0)
313     goto seek_failed;
314 #endif
315
316   return;
317
318   /* ERRORS */
319 flush_failed:
320   {
321     GST_DEBUG_OBJECT (filesink, "Flush failed: %s", g_strerror (errno));
322     return;
323   }
324 seek_failed:
325   {
326     GST_DEBUG_OBJECT (filesink, "Seeking failed: %s", g_strerror (errno));
327     return;
328   }
329 }
330
331 /* handle events (search) */
332 static gboolean
333 gst_file_sink_event (GstBaseSink * sink, GstEvent * event)
334 {
335   GstEventType type;
336   GstFileSink *filesink;
337
338   filesink = GST_FILE_SINK (sink);
339
340   type = GST_EVENT_TYPE (event);
341
342   switch (type) {
343     case GST_EVENT_NEWSEGMENT:
344     {
345       gint64 soffset, eoffset;
346       GstFormat format;
347
348       gst_event_parse_new_segment (event, NULL, NULL, &format, &soffset,
349           &eoffset, NULL);
350
351       if (format == GST_FORMAT_BYTES) {
352         gst_file_sink_do_seek (filesink, (guint64) soffset);
353       } else {
354         GST_DEBUG ("Ignored NEWSEGMENT event of format %u", (guint) format);
355       }
356       break;
357     }
358     case GST_EVENT_EOS:
359       if (fflush (filesink->file)) {
360         GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
361             (_("Error while writing to file \"%s\"."), filesink->filename),
362             GST_ERROR_SYSTEM);
363       }
364       break;
365     default:
366       break;
367   }
368
369   return TRUE;
370 }
371
372 static gboolean
373 gst_file_sink_get_current_offset (GstFileSink * filesink, guint64 * p_pos)
374 {
375   off_t ret;
376
377 #if HAVE_FTELLO
378   ret = ftello (filesink->file);
379 #elif G_OS_UNIX
380   if (fflush (filesink->file)) {
381     GST_DEBUG_OBJECT (filesink, "Flush failed: %s", g_strerror (errno));
382     /* ignore and continue */
383   }
384   ret = lseek (fileno (filesink->file), 0, SEEK_CUR);
385 #else
386   ret = (off_t) ftell (filesink->file);
387 #endif
388
389   *p_pos = (guint64) ret;
390
391   return (ret != (off_t) - 1);
392 }
393
394 static GstFlowReturn
395 gst_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
396 {
397   GstFileSink *filesink;
398   guint64 cur_pos;
399   guint size;
400   guint64 back_pending = 0;
401
402   size = GST_BUFFER_SIZE (buffer);
403
404   filesink = GST_FILE_SINK (sink);
405
406   if (!gst_file_sink_get_current_offset (filesink, &cur_pos))
407     goto handle_error;
408
409   if (cur_pos < filesink->data_written)
410     back_pending = filesink->data_written - cur_pos;
411
412   GST_DEBUG_OBJECT (filesink, "writing %u bytes at %" G_GUINT64_FORMAT,
413       size, cur_pos);
414
415   if (fwrite (GST_BUFFER_DATA (buffer), size, 1, filesink->file) != 1)
416     goto handle_error;
417
418   filesink->data_written += size - back_pending;
419
420   return GST_FLOW_OK;
421
422 handle_error:
423
424   GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
425       (_("Error while writing to file \"%s\"."), filesink->filename),
426       ("%s", g_strerror (errno)));
427
428   return GST_FLOW_ERROR;
429 }
430
431 static gboolean
432 gst_file_sink_start (GstBaseSink * basesink)
433 {
434   return gst_file_sink_open_file (GST_FILE_SINK (basesink));
435 }
436
437 static gboolean
438 gst_file_sink_stop (GstBaseSink * basesink)
439 {
440   gst_file_sink_close_file (GST_FILE_SINK (basesink));
441   return TRUE;
442 }
443
444 /*** GSTURIHANDLER INTERFACE *************************************************/
445
446 static guint
447 gst_file_sink_uri_get_type (void)
448 {
449   return GST_URI_SINK;
450 }
451 static gchar **
452 gst_file_sink_uri_get_protocols (void)
453 {
454   static gchar *protocols[] = { "file", NULL };
455
456   return protocols;
457 }
458 static const gchar *
459 gst_file_sink_uri_get_uri (GstURIHandler * handler)
460 {
461   GstFileSink *sink = GST_FILE_SINK (handler);
462
463   return sink->uri;
464 }
465
466 static gboolean
467 gst_file_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
468 {
469   gchar *protocol, *location;
470   gboolean ret;
471   GstFileSink *sink = GST_FILE_SINK (handler);
472
473   protocol = gst_uri_get_protocol (uri);
474   if (strcmp (protocol, "file") != 0) {
475     g_free (protocol);
476     return FALSE;
477   }
478   g_free (protocol);
479   location = gst_uri_get_location (uri);
480   ret = gst_file_sink_set_location (sink, location);
481   g_free (location);
482
483   return ret;
484 }
485
486 static void
487 gst_file_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
488 {
489   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
490
491   iface->get_type = gst_file_sink_uri_get_type;
492   iface->get_protocols = gst_file_sink_uri_get_protocols;
493   iface->get_uri = gst_file_sink_uri_get_uri;
494   iface->set_uri = gst_file_sink_uri_set_uri;
495 }