Merge remote-tracking branch 'remotes/origin/upstream/1.6' into tizen
[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  *                    2006 Wim Taymans <wim@fluendo.com>
5  *
6  * gstfilesink.c:
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 /**
24  * SECTION:element-filesink
25  * @see_also: #GstFileSrc
26  *
27  * Write incoming data to a file in the local file system.
28  *
29  * <refsect2>
30  * <title>Example launch line</title>
31  * |[
32  * gst-launch-1.0 v4l2src num-buffers=1 ! jpegenc ! filesink location=capture1.jpeg
33  * ]| Capture one frame from a v4l2 camera and save as jpeg image.
34  * </refsect2>
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #  include "config.h"
39 #endif
40
41 #include "../../gst/gst-i18n-lib.h"
42
43 #include <gst/gst.h>
44 #include <stdio.h>              /* for fseeko() */
45 #ifdef HAVE_STDIO_EXT_H
46 #include <stdio_ext.h>          /* for __fbufsize, for debugging */
47 #endif
48 #include <errno.h>
49 #include "gstfilesink.h"
50 #include <string.h>
51 #include <sys/types.h>
52
53 #ifdef G_OS_WIN32
54 #include <io.h>                 /* lseek, open, close, read */
55 #undef lseek
56 #define lseek _lseeki64
57 #undef off_t
58 #define off_t guint64
59 #undef ftruncate
60 #define ftruncate _chsize
61 #undef fsync
62 #define fsync _commit
63 #ifdef _MSC_VER                 /* Check if we are using MSVC, fileno is deprecated in favour */
64 #define fileno _fileno          /* of _fileno */
65 #endif
66 #endif
67
68 #include <sys/stat.h>
69 #ifdef HAVE_UNISTD_H
70 #include <unistd.h>
71 #endif
72
73 #include "gstelements_private.h"
74 #include "gstfilesink.h"
75
76 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
77     GST_PAD_SINK,
78     GST_PAD_ALWAYS,
79     GST_STATIC_CAPS_ANY);
80
81 #define GST_TYPE_FILE_SINK_BUFFER_MODE (gst_file_sink_buffer_mode_get_type ())
82 static GType
83 gst_file_sink_buffer_mode_get_type (void)
84 {
85   static GType buffer_mode_type = 0;
86   static const GEnumValue buffer_mode[] = {
87     {GST_FILE_SINK_BUFFER_MODE_DEFAULT, "Default buffering", "default"},
88     {GST_FILE_SINK_BUFFER_MODE_FULL, "Fully buffered", "full"},
89     {GST_FILE_SINK_BUFFER_MODE_LINE, "Line buffered", "line"},
90     {GST_FILE_SINK_BUFFER_MODE_UNBUFFERED, "Unbuffered", "unbuffered"},
91     {0, NULL, NULL},
92   };
93
94   if (!buffer_mode_type) {
95     buffer_mode_type =
96         g_enum_register_static ("GstFileSinkBufferMode", buffer_mode);
97   }
98   return buffer_mode_type;
99 }
100
101 GST_DEBUG_CATEGORY_STATIC (gst_file_sink_debug);
102 #define GST_CAT_DEFAULT gst_file_sink_debug
103
104 #define DEFAULT_LOCATION        NULL
105 #define DEFAULT_BUFFER_MODE     GST_FILE_SINK_BUFFER_MODE_DEFAULT
106 #define DEFAULT_BUFFER_SIZE     64 * 1024
107 #define DEFAULT_APPEND          FALSE
108
109 enum
110 {
111   PROP_0,
112   PROP_LOCATION,
113   PROP_BUFFER_MODE,
114   PROP_BUFFER_SIZE,
115   PROP_APPEND,
116 #ifdef GST_EXT_CURRENT_BYTES
117   PROP_CURRENT_BYTES,
118 #endif
119   PROP_LAST
120 };
121
122 /* Copy of glib's g_fopen due to win32 libc/cross-DLL brokenness: we can't
123  * use the 'file pointer' opened in glib (and returned from this function)
124  * in this library, as they may have unrelated C runtimes. */
125 static FILE *
126 gst_fopen (const gchar * filename, const gchar * mode)
127 {
128 #ifdef G_OS_WIN32
129   wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
130   wchar_t *wmode;
131   FILE *retval;
132   int save_errno;
133
134   if (wfilename == NULL) {
135     errno = EINVAL;
136     return NULL;
137   }
138
139   wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
140
141   if (wmode == NULL) {
142     g_free (wfilename);
143     errno = EINVAL;
144     return NULL;
145   }
146
147   retval = _wfopen (wfilename, wmode);
148   save_errno = errno;
149
150   g_free (wfilename);
151   g_free (wmode);
152
153   errno = save_errno;
154   return retval;
155 #else
156   return fopen (filename, mode);
157 #endif
158 }
159
160 static void gst_file_sink_dispose (GObject * object);
161
162 static void gst_file_sink_set_property (GObject * object, guint prop_id,
163     const GValue * value, GParamSpec * pspec);
164 static void gst_file_sink_get_property (GObject * object, guint prop_id,
165     GValue * value, GParamSpec * pspec);
166
167 static gboolean gst_file_sink_open_file (GstFileSink * sink);
168 static void gst_file_sink_close_file (GstFileSink * sink);
169
170 static gboolean gst_file_sink_start (GstBaseSink * sink);
171 static gboolean gst_file_sink_stop (GstBaseSink * sink);
172 static gboolean gst_file_sink_event (GstBaseSink * sink, GstEvent * event);
173 static GstFlowReturn gst_file_sink_render (GstBaseSink * sink,
174     GstBuffer * buffer);
175 static GstFlowReturn gst_file_sink_render_list (GstBaseSink * sink,
176     GstBufferList * list);
177
178 static gboolean gst_file_sink_do_seek (GstFileSink * filesink,
179     guint64 new_offset);
180 static gboolean gst_file_sink_get_current_offset (GstFileSink * filesink,
181     guint64 * p_pos);
182
183 static gboolean gst_file_sink_query (GstBaseSink * bsink, GstQuery * query);
184
185 static void gst_file_sink_uri_handler_init (gpointer g_iface,
186     gpointer iface_data);
187
188 #define _do_init \
189   G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_file_sink_uri_handler_init); \
190   GST_DEBUG_CATEGORY_INIT (gst_file_sink_debug, "filesink", 0, "filesink element");
191 #define gst_file_sink_parent_class parent_class
192 G_DEFINE_TYPE_WITH_CODE (GstFileSink, gst_file_sink, GST_TYPE_BASE_SINK,
193     _do_init);
194
195 static void
196 gst_file_sink_class_init (GstFileSinkClass * klass)
197 {
198   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
199   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
200   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
201
202   gobject_class->dispose = gst_file_sink_dispose;
203
204   gobject_class->set_property = gst_file_sink_set_property;
205   gobject_class->get_property = gst_file_sink_get_property;
206
207   g_object_class_install_property (gobject_class, PROP_LOCATION,
208       g_param_spec_string ("location", "File Location",
209           "Location of the file to write", NULL,
210           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
211
212   g_object_class_install_property (gobject_class, PROP_BUFFER_MODE,
213       g_param_spec_enum ("buffer-mode", "Buffering mode",
214           "The buffering mode to use", GST_TYPE_FILE_SINK_BUFFER_MODE,
215           DEFAULT_BUFFER_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
216
217   g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
218       g_param_spec_uint ("buffer-size", "Buffering size",
219           "Size of buffer in number of bytes for line or full buffer-mode", 0,
220           G_MAXUINT, DEFAULT_BUFFER_SIZE,
221           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
222 #ifdef GST_EXT_CURRENT_BYTES
223   g_object_class_install_property (gobject_class, PROP_CURRENT_BYTES,
224       g_param_spec_uint64 ("current-bytes", "Current bytes",
225           "downloaded bytes so far", 0,
226           G_MAXUINT64, 0,
227           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
228 #endif
229
230   /**
231    * GstFileSink:append
232    * 
233    * Append to an already existing file.
234    */
235   g_object_class_install_property (gobject_class, PROP_APPEND,
236       g_param_spec_boolean ("append", "Append",
237           "Append to an already existing file", DEFAULT_APPEND,
238           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
239
240   gst_element_class_set_static_metadata (gstelement_class,
241       "File Sink",
242       "Sink/File", "Write stream to a file",
243       "Thomas Vander Stichele <thomas at apestaart dot org>");
244   gst_element_class_add_pad_template (gstelement_class,
245       gst_static_pad_template_get (&sinktemplate));
246
247   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_file_sink_start);
248   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_file_sink_stop);
249   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_file_sink_query);
250   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_file_sink_render);
251   gstbasesink_class->render_list =
252       GST_DEBUG_FUNCPTR (gst_file_sink_render_list);
253   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_file_sink_event);
254
255   if (sizeof (off_t) < 8) {
256     GST_LOG ("No large file support, sizeof (off_t) = %" G_GSIZE_FORMAT "!",
257         sizeof (off_t));
258   }
259 }
260
261 static void
262 gst_file_sink_init (GstFileSink * filesink)
263 {
264   filesink->filename = NULL;
265   filesink->file = NULL;
266   filesink->current_pos = 0;
267   filesink->buffer_mode = DEFAULT_BUFFER_MODE;
268   filesink->buffer_size = DEFAULT_BUFFER_SIZE;
269   filesink->buffer = NULL;
270   filesink->append = FALSE;
271
272   gst_base_sink_set_sync (GST_BASE_SINK (filesink), FALSE);
273 }
274
275 static void
276 gst_file_sink_dispose (GObject * object)
277 {
278   GstFileSink *sink = GST_FILE_SINK (object);
279
280   G_OBJECT_CLASS (parent_class)->dispose (object);
281
282   g_free (sink->uri);
283   sink->uri = NULL;
284   g_free (sink->filename);
285   sink->filename = NULL;
286   g_free (sink->buffer);
287   sink->buffer = NULL;
288   sink->buffer_size = 0;
289 }
290
291 static gboolean
292 gst_file_sink_set_location (GstFileSink * sink, const gchar * location,
293     GError ** error)
294 {
295   if (sink->file)
296     goto was_open;
297
298   g_free (sink->filename);
299   g_free (sink->uri);
300   if (location != NULL) {
301     /* we store the filename as we received it from the application. On Windows
302      * this should be in UTF8 */
303     sink->filename = g_strdup (location);
304     sink->uri = gst_filename_to_uri (location, NULL);
305     GST_INFO_OBJECT (sink, "filename : %s", sink->filename);
306     GST_INFO_OBJECT (sink, "uri      : %s", sink->uri);
307   } else {
308     sink->filename = NULL;
309     sink->uri = NULL;
310   }
311
312   return TRUE;
313
314   /* ERRORS */
315 was_open:
316   {
317     g_warning ("Changing the `location' property on filesink when a file is "
318         "open is not supported.");
319     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
320         "Changing the 'location' property on filesink when a file is "
321         "open is not supported");
322     return FALSE;
323   }
324 }
325
326 static void
327 gst_file_sink_set_property (GObject * object, guint prop_id,
328     const GValue * value, GParamSpec * pspec)
329 {
330   GstFileSink *sink = GST_FILE_SINK (object);
331
332   switch (prop_id) {
333     case PROP_LOCATION:
334       gst_file_sink_set_location (sink, g_value_get_string (value), NULL);
335       break;
336     case PROP_BUFFER_MODE:
337       sink->buffer_mode = g_value_get_enum (value);
338       break;
339     case PROP_BUFFER_SIZE:
340       sink->buffer_size = g_value_get_uint (value);
341       break;
342     case PROP_APPEND:
343       sink->append = g_value_get_boolean (value);
344       break;
345     default:
346       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
347       break;
348   }
349 }
350
351 static void
352 gst_file_sink_get_property (GObject * object, guint prop_id, GValue * value,
353     GParamSpec * pspec)
354 {
355   GstFileSink *sink = GST_FILE_SINK (object);
356
357   switch (prop_id) {
358     case PROP_LOCATION:
359       g_value_set_string (value, sink->filename);
360       break;
361     case PROP_BUFFER_MODE:
362       g_value_set_enum (value, sink->buffer_mode);
363       break;
364     case PROP_BUFFER_SIZE:
365       g_value_set_uint (value, sink->buffer_size);
366       break;
367     case PROP_APPEND:
368       g_value_set_boolean (value, sink->append);
369       break;
370 #ifdef GST_EXT_CURRENT_BYTES
371     case PROP_CURRENT_BYTES:
372       g_value_set_uint64(value, sink->current_pos);
373       break;
374 #endif
375     default:
376       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
377       break;
378   }
379 }
380
381 static gboolean
382 gst_file_sink_open_file (GstFileSink * sink)
383 {
384   gint mode;
385
386   /* open the file */
387   if (sink->filename == NULL || sink->filename[0] == '\0')
388     goto no_filename;
389
390   if (sink->append)
391     sink->file = gst_fopen (sink->filename, "ab");
392   else
393     sink->file = gst_fopen (sink->filename, "wb");
394   if (sink->file == NULL)
395     goto open_failed;
396
397   /* see if we are asked to perform a specific kind of buffering */
398   if ((mode = sink->buffer_mode) != -1) {
399     guint buffer_size;
400
401     /* free previous buffer if any */
402     g_free (sink->buffer);
403
404     if (mode == _IONBF) {
405       /* no buffering */
406       sink->buffer = NULL;
407       buffer_size = 0;
408     } else {
409       /* allocate buffer */
410       sink->buffer = g_malloc (sink->buffer_size);
411       buffer_size = sink->buffer_size;
412     }
413     /* Cygwin does not have __fbufsize */
414 #if defined(HAVE_STDIO_EXT_H) && !defined(__CYGWIN__)
415     GST_DEBUG_OBJECT (sink, "change buffer size %u to %u, mode %d",
416         (guint) __fbufsize (sink->file), buffer_size, mode);
417 #else
418     GST_DEBUG_OBJECT (sink, "change  buffer size to %u, mode %d",
419         sink->buffer_size, mode);
420 #endif
421     if (setvbuf (sink->file, sink->buffer, mode, buffer_size) != 0) {
422       GST_WARNING_OBJECT (sink, "warning: setvbuf failed: %s",
423           g_strerror (errno));
424     }
425   }
426
427   sink->current_pos = 0;
428   /* try to seek in the file to figure out if it is seekable */
429   sink->seekable = gst_file_sink_do_seek (sink, 0);
430
431   GST_DEBUG_OBJECT (sink, "opened file %s, seekable %d",
432       sink->filename, sink->seekable);
433
434   return TRUE;
435
436   /* ERRORS */
437 no_filename:
438   {
439     GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
440         (_("No file name specified for writing.")), (NULL));
441     return FALSE;
442   }
443 open_failed:
444   {
445     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
446         (_("Could not open file \"%s\" for writing."), sink->filename),
447         GST_ERROR_SYSTEM);
448     return FALSE;
449   }
450 }
451
452 static void
453 gst_file_sink_close_file (GstFileSink * sink)
454 {
455   if (sink->file) {
456     if (fclose (sink->file) != 0)
457       goto close_failed;
458
459     GST_DEBUG_OBJECT (sink, "closed file");
460     sink->file = NULL;
461
462     g_free (sink->buffer);
463     sink->buffer = NULL;
464   }
465   return;
466
467   /* ERRORS */
468 close_failed:
469   {
470     GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
471         (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
472     return;
473   }
474 }
475
476 static gboolean
477 gst_file_sink_query (GstBaseSink * bsink, GstQuery * query)
478 {
479   gboolean res;
480   GstFileSink *self;
481   GstFormat format;
482
483   self = GST_FILE_SINK (bsink);
484
485   switch (GST_QUERY_TYPE (query)) {
486     case GST_QUERY_POSITION:
487       gst_query_parse_position (query, &format, NULL);
488
489       switch (format) {
490         case GST_FORMAT_DEFAULT:
491         case GST_FORMAT_BYTES:
492           gst_query_set_position (query, GST_FORMAT_BYTES, self->current_pos);
493           res = TRUE;
494           break;
495         default:
496           res = FALSE;
497           break;
498       }
499       break;
500
501     case GST_QUERY_FORMATS:
502       gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
503       res = TRUE;
504       break;
505
506     case GST_QUERY_URI:
507       gst_query_set_uri (query, self->uri);
508       res = TRUE;
509       break;
510
511     case GST_QUERY_SEEKING:
512       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
513       if (format == GST_FORMAT_BYTES || format == GST_FORMAT_DEFAULT) {
514         gst_query_set_seeking (query, GST_FORMAT_BYTES, self->seekable, 0, -1);
515       } else {
516         gst_query_set_seeking (query, format, FALSE, 0, -1);
517       }
518       res = TRUE;
519       break;
520
521     default:
522       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
523       break;
524   }
525   return res;
526 }
527
528 #ifdef HAVE_FSEEKO
529 # define __GST_STDIO_SEEK_FUNCTION "fseeko"
530 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
531 # define __GST_STDIO_SEEK_FUNCTION "lseek"
532 #else
533 # define __GST_STDIO_SEEK_FUNCTION "fseek"
534 #endif
535
536 static gboolean
537 gst_file_sink_do_seek (GstFileSink * filesink, guint64 new_offset)
538 {
539   GST_DEBUG_OBJECT (filesink, "Seeking to offset %" G_GUINT64_FORMAT
540       " using " __GST_STDIO_SEEK_FUNCTION, new_offset);
541
542   if (fflush (filesink->file))
543     goto flush_failed;
544
545 #ifdef HAVE_FSEEKO
546   if (fseeko (filesink->file, (off_t) new_offset, SEEK_SET) != 0)
547     goto seek_failed;
548 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
549   if (lseek (fileno (filesink->file), (off_t) new_offset,
550           SEEK_SET) == (off_t) - 1)
551     goto seek_failed;
552 #else
553   if (fseek (filesink->file, (long) new_offset, SEEK_SET) != 0)
554     goto seek_failed;
555 #endif
556
557   /* adjust position reporting after seek;
558    * presumably this should basically yield new_offset */
559   gst_file_sink_get_current_offset (filesink, &filesink->current_pos);
560
561   return TRUE;
562
563   /* ERRORS */
564 flush_failed:
565   {
566     GST_DEBUG_OBJECT (filesink, "Flush failed: %s", g_strerror (errno));
567     return FALSE;
568   }
569 seek_failed:
570   {
571     GST_DEBUG_OBJECT (filesink, "Seeking failed: %s", g_strerror (errno));
572     return FALSE;
573   }
574 }
575
576 /* handle events (search) */
577 static gboolean
578 gst_file_sink_event (GstBaseSink * sink, GstEvent * event)
579 {
580   GstEventType type;
581   GstFileSink *filesink;
582
583   filesink = GST_FILE_SINK (sink);
584
585   type = GST_EVENT_TYPE (event);
586
587   switch (type) {
588     case GST_EVENT_SEGMENT:
589     {
590       const GstSegment *segment;
591
592       gst_event_parse_segment (event, &segment);
593
594       if (segment->format == GST_FORMAT_BYTES) {
595         /* only try to seek and fail when we are going to a different
596          * position */
597         if (filesink->current_pos != segment->start) {
598           /* FIXME, the seek should be performed on the pos field, start/stop are
599            * just boundaries for valid bytes offsets. We should also fill the file
600            * with zeroes if the new position extends the current EOF (sparse streams
601            * and segment accumulation). */
602           if (!gst_file_sink_do_seek (filesink, (guint64) segment->start))
603             goto seek_failed;
604         } else {
605           GST_DEBUG_OBJECT (filesink, "Ignored SEGMENT, no seek needed");
606         }
607       } else {
608         GST_DEBUG_OBJECT (filesink,
609             "Ignored SEGMENT event of format %u (%s)", (guint) segment->format,
610             gst_format_get_name (segment->format));
611       }
612       break;
613     }
614     case GST_EVENT_FLUSH_STOP:
615       if (filesink->current_pos != 0 && filesink->seekable) {
616         gst_file_sink_do_seek (filesink, 0);
617         if (ftruncate (fileno (filesink->file), 0))
618           goto flush_failed;
619       }
620       break;
621     case GST_EVENT_EOS:
622       if (fflush (filesink->file))
623         goto flush_failed;
624       break;
625     default:
626       break;
627   }
628
629   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
630
631   /* ERRORS */
632 seek_failed:
633   {
634     GST_ELEMENT_ERROR (filesink, RESOURCE, SEEK,
635         (_("Error while seeking in file \"%s\"."), filesink->filename),
636         GST_ERROR_SYSTEM);
637     gst_event_unref (event);
638     return FALSE;
639   }
640 flush_failed:
641   {
642     GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
643         (_("Error while writing to file \"%s\"."), filesink->filename),
644         GST_ERROR_SYSTEM);
645     gst_event_unref (event);
646     return FALSE;
647   }
648 }
649
650 static gboolean
651 gst_file_sink_get_current_offset (GstFileSink * filesink, guint64 * p_pos)
652 {
653   off_t ret = -1;
654
655 #ifdef HAVE_FTELLO
656   ret = ftello (filesink->file);
657 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
658   if (fflush (filesink->file)) {
659     GST_DEBUG_OBJECT (filesink, "Flush failed: %s", g_strerror (errno));
660     /* ignore and continue */
661   }
662   ret = lseek (fileno (filesink->file), 0, SEEK_CUR);
663 #else
664   ret = (off_t) ftell (filesink->file);
665 #endif
666
667   if (ret != (off_t) - 1)
668     *p_pos = (guint64) ret;
669
670   return (ret != (off_t) - 1);
671 }
672
673 static GstFlowReturn
674 gst_file_sink_render_buffers (GstFileSink * sink, GstBuffer ** buffers,
675     guint num_buffers, guint8 * mem_nums, guint total_mems)
676 {
677   GST_DEBUG_OBJECT (sink,
678       "writing %u buffers (%u memories) at position %" G_GUINT64_FORMAT,
679       num_buffers, total_mems, sink->current_pos);
680
681   return gst_writev_buffers (GST_OBJECT_CAST (sink), fileno (sink->file), NULL,
682       buffers, num_buffers, mem_nums, total_mems, NULL, &sink->current_pos);
683 }
684
685 static GstFlowReturn
686 gst_file_sink_render_list (GstBaseSink * bsink, GstBufferList * buffer_list)
687 {
688   GstFlowReturn flow;
689   GstBuffer **buffers;
690   GstFileSink *sink;
691   guint8 *mem_nums;
692   guint total_mems;
693   guint i, num_buffers;
694   gboolean sync_after = FALSE;
695
696   sink = GST_FILE_SINK_CAST (bsink);
697
698   num_buffers = gst_buffer_list_length (buffer_list);
699   if (num_buffers == 0)
700     goto no_data;
701
702   /* extract buffers from list and count memories */
703   buffers = g_newa (GstBuffer *, num_buffers);
704   mem_nums = g_newa (guint8, num_buffers);
705   for (i = 0, total_mems = 0; i < num_buffers; ++i) {
706     buffers[i] = gst_buffer_list_get (buffer_list, i);
707     mem_nums[i] = gst_buffer_n_memory (buffers[i]);
708     total_mems += mem_nums[i];
709     if (GST_BUFFER_FLAG_IS_SET (buffers[i], GST_BUFFER_FLAG_SYNC_AFTER))
710       sync_after = TRUE;
711   }
712
713   flow =
714       gst_file_sink_render_buffers (sink, buffers, num_buffers, mem_nums,
715       total_mems);
716
717   if (flow == GST_FLOW_OK && sync_after) {
718     if (fflush (sink->file) || fsync (fileno (sink->file))) {
719       GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
720           (_("Error while writing to file \"%s\"."), sink->filename),
721           ("%s", g_strerror (errno)));
722       flow = GST_FLOW_ERROR;
723     }
724   }
725
726   return flow;
727
728 no_data:
729   {
730     GST_LOG_OBJECT (sink, "empty buffer list");
731     return GST_FLOW_OK;
732   }
733 }
734
735 static GstFlowReturn
736 gst_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
737 {
738   GstFileSink *filesink;
739   GstFlowReturn flow;
740   guint8 n_mem;
741
742   filesink = GST_FILE_SINK_CAST (sink);
743
744   n_mem = gst_buffer_n_memory (buffer);
745
746   if (n_mem > 0)
747     flow = gst_file_sink_render_buffers (filesink, &buffer, 1, &n_mem, n_mem);
748   else
749     flow = GST_FLOW_OK;
750
751   if (flow == GST_FLOW_OK &&
752       GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_SYNC_AFTER)) {
753     if (fflush (filesink->file) || fsync (fileno (filesink->file))) {
754       GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
755           (_("Error while writing to file \"%s\"."), filesink->filename),
756           ("%s", g_strerror (errno)));
757       flow = GST_FLOW_ERROR;
758     }
759   }
760
761   return flow;
762 }
763
764 static gboolean
765 gst_file_sink_start (GstBaseSink * basesink)
766 {
767   return gst_file_sink_open_file (GST_FILE_SINK (basesink));
768 }
769
770 static gboolean
771 gst_file_sink_stop (GstBaseSink * basesink)
772 {
773   gst_file_sink_close_file (GST_FILE_SINK (basesink));
774   return TRUE;
775 }
776
777 /*** GSTURIHANDLER INTERFACE *************************************************/
778
779 static GstURIType
780 gst_file_sink_uri_get_type (GType type)
781 {
782   return GST_URI_SINK;
783 }
784
785 static const gchar *const *
786 gst_file_sink_uri_get_protocols (GType type)
787 {
788   static const gchar *protocols[] = { "file", NULL };
789
790   return protocols;
791 }
792
793 static gchar *
794 gst_file_sink_uri_get_uri (GstURIHandler * handler)
795 {
796   GstFileSink *sink = GST_FILE_SINK (handler);
797
798   /* FIXME: make thread-safe */
799   return g_strdup (sink->uri);
800 }
801
802 static gboolean
803 gst_file_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri,
804     GError ** error)
805 {
806   gchar *location;
807   gboolean ret;
808   GstFileSink *sink = GST_FILE_SINK (handler);
809
810   /* allow file://localhost/foo/bar by stripping localhost but fail
811    * for every other hostname */
812   if (g_str_has_prefix (uri, "file://localhost/")) {
813     char *tmp;
814
815     /* 16 == strlen ("file://localhost") */
816     tmp = g_strconcat ("file://", uri + 16, NULL);
817     /* we use gst_uri_get_location() although we already have the
818      * "location" with uri + 16 because it provides unescaping */
819     location = gst_uri_get_location (tmp);
820     g_free (tmp);
821   } else if (strcmp (uri, "file://") == 0) {
822     /* Special case for "file://" as this is used by some applications
823      *  to test with gst_element_make_from_uri if there's an element
824      *  that supports the URI protocol. */
825     gst_file_sink_set_location (sink, NULL, NULL);
826     return TRUE;
827   } else {
828     location = gst_uri_get_location (uri);
829   }
830
831   if (!location) {
832     g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
833         "File URI without location");
834     return FALSE;
835   }
836
837   if (!g_path_is_absolute (location)) {
838     g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
839         "File URI location must be an absolute path");
840     g_free (location);
841     return FALSE;
842   }
843
844   ret = gst_file_sink_set_location (sink, location, error);
845   g_free (location);
846
847   return ret;
848 }
849
850 static void
851 gst_file_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
852 {
853   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
854
855   iface->get_type = gst_file_sink_uri_get_type;
856   iface->get_protocols = gst_file_sink_uri_get_protocols;
857   iface->get_uri = gst_file_sink_uri_get_uri;
858   iface->set_uri = gst_file_sink_uri_set_uri;
859 }