filesink: fix handling of non-existing paths with musl
[platform/upstream/gstreamer.git] / subprojects / gstreamer / 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  * @title: filesink
26  * @see_also: #GstFileSrc
27  *
28  * Write incoming data to a file in the local file system.
29  *
30  * ## Example launch line
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  *
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #  include "config.h"
39 #endif
40
41 #include <glib/gi18n-lib.h>
42
43 #include <gst/gst.h>
44 #include <glib/gstdio.h>
45 #include <stdio.h>              /* for fseeko() */
46 #ifdef HAVE_STDIO_EXT_H
47 #include <stdio_ext.h>          /* for __fbufsize, for debugging */
48 #endif
49 #include <errno.h>
50 #include "gstfilesink.h"
51 #include <string.h>
52 #include <sys/types.h>
53 #include <fcntl.h>
54
55 #ifdef G_OS_WIN32
56 #include <io.h>                 /* lseek, open, close, read */
57 #undef lseek
58 #define lseek _lseeki64
59 #undef off_t
60 #define off_t guint64
61 #undef ftruncate
62 #define ftruncate _chsize
63 #undef fsync
64 #define fsync _commit
65 #ifdef _MSC_VER                 /* Check if we are using MSVC, fileno is deprecated in favour */
66 #define fileno _fileno          /* of _fileno */
67 #endif
68 #endif
69
70 #include <sys/stat.h>
71 #ifdef HAVE_UNISTD_H
72 #include <unistd.h>
73 #endif
74
75 #include "gstelements_private.h"
76 #include "gstfilesink.h"
77 #include "gstcoreelementselements.h"
78
79 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
80     GST_PAD_SINK,
81     GST_PAD_ALWAYS,
82     GST_STATIC_CAPS_ANY);
83
84 #define GST_TYPE_FILE_SINK_BUFFER_MODE (gst_file_sink_buffer_mode_get_type ())
85 static GType
86 gst_file_sink_buffer_mode_get_type (void)
87 {
88   static GType buffer_mode_type = 0;
89   static const GEnumValue buffer_mode[] = {
90     {GST_FILE_SINK_BUFFER_MODE_DEFAULT, "Default buffering", "default"},
91     {GST_FILE_SINK_BUFFER_MODE_FULL, "Fully buffered", "full"},
92     {GST_FILE_SINK_BUFFER_MODE_LINE, "Line buffered (deprecated, like full)",
93         "line"},
94     {GST_FILE_SINK_BUFFER_MODE_UNBUFFERED, "Unbuffered", "unbuffered"},
95     {0, NULL, NULL},
96   };
97
98   if (!buffer_mode_type) {
99     buffer_mode_type =
100         g_enum_register_static ("GstFileSinkBufferMode", buffer_mode);
101   }
102   return buffer_mode_type;
103 }
104
105 GST_DEBUG_CATEGORY_STATIC (gst_file_sink_debug);
106 #define GST_CAT_DEFAULT gst_file_sink_debug
107
108 #define DEFAULT_LOCATION        NULL
109 #define DEFAULT_BUFFER_MODE     GST_FILE_SINK_BUFFER_MODE_DEFAULT
110 #define DEFAULT_BUFFER_SIZE     64 * 1024
111 #define DEFAULT_APPEND          FALSE
112 #define DEFAULT_O_SYNC          FALSE
113 #define DEFAULT_MAX_TRANSIENT_ERROR_TIMEOUT     0
114
115 enum
116 {
117   PROP_0,
118   PROP_LOCATION,
119   PROP_BUFFER_MODE,
120   PROP_BUFFER_SIZE,
121   PROP_APPEND,
122   PROP_O_SYNC,
123   PROP_MAX_TRANSIENT_ERROR_TIMEOUT,
124   PROP_LAST
125 };
126
127 /* Copy of glib's g_fopen due to win32 libc/cross-DLL brokenness: we can't
128  * use the 'file pointer' opened in glib (and returned from this function)
129  * in this library, as they may have unrelated C runtimes. */
130 static FILE *
131 gst_fopen (const gchar * filename, const gchar * mode, gboolean o_sync)
132 {
133   FILE *retval;
134 #ifdef G_OS_WIN32
135   retval = g_fopen (filename, mode);
136   return retval;
137 #else
138   int fd;
139   int flags = O_CREAT | O_WRONLY;
140
141   /* NOTE: below code is for handing spurious EACCES return on write
142    * See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/143
143    */
144   if (strcmp (mode, "wb") == 0)
145     flags |= O_TRUNC;
146   else if (strcmp (mode, "ab") == 0)
147     flags |= O_APPEND;
148   else
149     g_assert_not_reached ();
150
151   if (o_sync)
152     flags |= O_SYNC;
153
154   fd = open (filename, flags, 0666);
155
156   if (fd < 0)
157     return NULL;
158
159   retval = fdopen (fd, mode);
160   return retval;
161 #endif
162 }
163
164 static void gst_file_sink_dispose (GObject * object);
165
166 static void gst_file_sink_set_property (GObject * object, guint prop_id,
167     const GValue * value, GParamSpec * pspec);
168 static void gst_file_sink_get_property (GObject * object, guint prop_id,
169     GValue * value, GParamSpec * pspec);
170
171 static gboolean gst_file_sink_open_file (GstFileSink * sink);
172 static void gst_file_sink_close_file (GstFileSink * sink);
173
174 static gboolean gst_file_sink_start (GstBaseSink * sink);
175 static gboolean gst_file_sink_stop (GstBaseSink * sink);
176 static gboolean gst_file_sink_event (GstBaseSink * sink, GstEvent * event);
177 static GstFlowReturn gst_file_sink_render (GstBaseSink * sink,
178     GstBuffer * buffer);
179 static GstFlowReturn gst_file_sink_render_list (GstBaseSink * sink,
180     GstBufferList * list);
181 static gboolean gst_file_sink_unlock (GstBaseSink * sink);
182 static gboolean gst_file_sink_unlock_stop (GstBaseSink * sink);
183
184 static gboolean gst_file_sink_do_seek (GstFileSink * filesink,
185     guint64 new_offset);
186 static gboolean gst_file_sink_get_current_offset (GstFileSink * filesink,
187     guint64 * p_pos);
188
189 static gboolean gst_file_sink_query (GstBaseSink * bsink, GstQuery * query);
190
191 static void gst_file_sink_uri_handler_init (gpointer g_iface,
192     gpointer iface_data);
193
194 static GstFlowReturn gst_file_sink_flush_buffer (GstFileSink * filesink);
195
196 #define _do_init \
197   G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_file_sink_uri_handler_init); \
198   GST_DEBUG_CATEGORY_INIT (gst_file_sink_debug, "filesink", 0, "filesink element");
199 #define gst_file_sink_parent_class parent_class
200 G_DEFINE_TYPE_WITH_CODE (GstFileSink, gst_file_sink, GST_TYPE_BASE_SINK,
201     _do_init);
202 GST_ELEMENT_REGISTER_DEFINE (filesink, "filesink", GST_RANK_PRIMARY,
203     GST_TYPE_FILE_SINK);
204
205 static void
206 gst_file_sink_class_init (GstFileSinkClass * klass)
207 {
208   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
209   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
210   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
211
212   gobject_class->dispose = gst_file_sink_dispose;
213
214   gobject_class->set_property = gst_file_sink_set_property;
215   gobject_class->get_property = gst_file_sink_get_property;
216
217   g_object_class_install_property (gobject_class, PROP_LOCATION,
218       g_param_spec_string ("location", "File Location",
219           "Location of the file to write", NULL,
220           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221
222   g_object_class_install_property (gobject_class, PROP_BUFFER_MODE,
223       g_param_spec_enum ("buffer-mode", "Buffering mode",
224           "The buffering mode to use", GST_TYPE_FILE_SINK_BUFFER_MODE,
225           DEFAULT_BUFFER_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
226
227   g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
228       g_param_spec_uint ("buffer-size", "Buffering size",
229           "Size of buffer in number of bytes for line or full buffer-mode", 0,
230           G_MAXUINT, DEFAULT_BUFFER_SIZE,
231           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
232
233   /**
234    * GstFileSink:append
235    *
236    * Append to an already existing file.
237    */
238   g_object_class_install_property (gobject_class, PROP_APPEND,
239       g_param_spec_boolean ("append", "Append",
240           "Append to an already existing file", DEFAULT_APPEND,
241           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
242
243   g_object_class_install_property (gobject_class, PROP_O_SYNC,
244       g_param_spec_boolean ("o-sync", "Synchronous IO",
245           "Open the file with O_SYNC for enabling synchronous IO",
246           DEFAULT_O_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
247
248   g_object_class_install_property (gobject_class,
249       PROP_MAX_TRANSIENT_ERROR_TIMEOUT,
250       g_param_spec_int ("max-transient-error-timeout",
251           "Max Transient Error Timeout",
252           "Retry up to this many ms on transient errors (currently EACCES)", 0,
253           G_MAXINT, DEFAULT_MAX_TRANSIENT_ERROR_TIMEOUT,
254           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
255
256   gst_element_class_set_static_metadata (gstelement_class,
257       "File Sink",
258       "Sink/File", "Write stream to a file",
259       "Thomas Vander Stichele <thomas at apestaart dot org>");
260   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
261
262   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_file_sink_start);
263   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_file_sink_stop);
264   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_file_sink_query);
265   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_file_sink_render);
266   gstbasesink_class->render_list =
267       GST_DEBUG_FUNCPTR (gst_file_sink_render_list);
268   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_file_sink_event);
269   gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_file_sink_unlock);
270   gstbasesink_class->unlock_stop =
271       GST_DEBUG_FUNCPTR (gst_file_sink_unlock_stop);
272
273   if (sizeof (off_t) < 8) {
274     GST_LOG ("No large file support, sizeof (off_t) = %" G_GSIZE_FORMAT "!",
275         sizeof (off_t));
276   }
277
278   gst_type_mark_as_plugin_api (GST_TYPE_FILE_SINK_BUFFER_MODE, 0);
279 }
280
281 static void
282 gst_file_sink_init (GstFileSink * filesink)
283 {
284   filesink->filename = NULL;
285   filesink->file = NULL;
286   filesink->current_pos = 0;
287   filesink->buffer_mode = DEFAULT_BUFFER_MODE;
288   filesink->buffer_size = DEFAULT_BUFFER_SIZE;
289   filesink->append = FALSE;
290
291   gst_base_sink_set_sync (GST_BASE_SINK (filesink), FALSE);
292 }
293
294 static void
295 gst_file_sink_dispose (GObject * object)
296 {
297   GstFileSink *sink = GST_FILE_SINK (object);
298
299   G_OBJECT_CLASS (parent_class)->dispose (object);
300
301   g_free (sink->uri);
302   sink->uri = NULL;
303   g_free (sink->filename);
304   sink->filename = NULL;
305 }
306
307 static gboolean
308 gst_file_sink_set_location (GstFileSink * sink, const gchar * location,
309     GError ** error)
310 {
311   if (sink->file)
312     goto was_open;
313
314   g_free (sink->filename);
315   g_free (sink->uri);
316   if (location != NULL) {
317     /* we store the filename as we received it from the application. On Windows
318      * this should be in UTF8 */
319     sink->filename = g_strdup (location);
320     sink->uri = gst_filename_to_uri (location, NULL);
321     GST_INFO_OBJECT (sink, "filename : %s", sink->filename);
322     GST_INFO_OBJECT (sink, "uri      : %s", sink->uri);
323   } else {
324     sink->filename = NULL;
325     sink->uri = NULL;
326   }
327
328   return TRUE;
329
330   /* ERRORS */
331 was_open:
332   {
333     g_warning ("Changing the `location' property on filesink when a file is "
334         "open is not supported.");
335     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
336         "Changing the 'location' property on filesink when a file is "
337         "open is not supported");
338     return FALSE;
339   }
340 }
341
342 static void
343 gst_file_sink_set_property (GObject * object, guint prop_id,
344     const GValue * value, GParamSpec * pspec)
345 {
346   GstFileSink *sink = GST_FILE_SINK (object);
347
348   switch (prop_id) {
349     case PROP_LOCATION:
350       gst_file_sink_set_location (sink, g_value_get_string (value), NULL);
351       break;
352     case PROP_BUFFER_MODE:
353       sink->buffer_mode = g_value_get_enum (value);
354       break;
355     case PROP_BUFFER_SIZE:
356       sink->buffer_size = g_value_get_uint (value);
357       break;
358     case PROP_APPEND:
359       sink->append = g_value_get_boolean (value);
360       break;
361     case PROP_O_SYNC:
362       sink->o_sync = g_value_get_boolean (value);
363       break;
364     case PROP_MAX_TRANSIENT_ERROR_TIMEOUT:
365       sink->max_transient_error_timeout = g_value_get_int (value);
366       break;
367     default:
368       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
369       break;
370   }
371 }
372
373 static void
374 gst_file_sink_get_property (GObject * object, guint prop_id, GValue * value,
375     GParamSpec * pspec)
376 {
377   GstFileSink *sink = GST_FILE_SINK (object);
378
379   switch (prop_id) {
380     case PROP_LOCATION:
381       g_value_set_string (value, sink->filename);
382       break;
383     case PROP_BUFFER_MODE:
384       g_value_set_enum (value, sink->buffer_mode);
385       break;
386     case PROP_BUFFER_SIZE:
387       g_value_set_uint (value, sink->buffer_size);
388       break;
389     case PROP_APPEND:
390       g_value_set_boolean (value, sink->append);
391       break;
392     case PROP_O_SYNC:
393       g_value_set_boolean (value, sink->o_sync);
394       break;
395     case PROP_MAX_TRANSIENT_ERROR_TIMEOUT:
396       g_value_set_int (value, sink->max_transient_error_timeout);
397       break;
398     default:
399       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400       break;
401   }
402 }
403
404 static gboolean
405 gst_file_sink_open_file (GstFileSink * sink)
406 {
407   /* open the file */
408   if (sink->filename == NULL || sink->filename[0] == '\0')
409     goto no_filename;
410
411   if (sink->append)
412     sink->file = gst_fopen (sink->filename, "ab", sink->o_sync);
413   else
414     sink->file = gst_fopen (sink->filename, "wb", sink->o_sync);
415   if (sink->file == NULL)
416     goto open_failed;
417
418   sink->current_pos = 0;
419   /* try to seek in the file to figure out if it is seekable */
420   sink->seekable = gst_file_sink_do_seek (sink, 0);
421
422   if (sink->buffer)
423     g_free (sink->buffer);
424   sink->buffer = NULL;
425   if (sink->buffer_list)
426     gst_buffer_list_unref (sink->buffer_list);
427   sink->buffer_list = NULL;
428
429   if (sink->buffer_mode != GST_FILE_SINK_BUFFER_MODE_UNBUFFERED) {
430     if (sink->buffer_size == 0) {
431       sink->buffer_size = DEFAULT_BUFFER_SIZE;
432       g_object_notify (G_OBJECT (sink), "buffer-size");
433     }
434
435     if (sink->buffer_mode == GST_FILE_SINK_BUFFER_MODE_FULL) {
436       sink->buffer = g_malloc (sink->buffer_size);
437       sink->allocated_buffer_size = sink->buffer_size;
438     } else {
439       sink->buffer_list = gst_buffer_list_new ();
440     }
441     sink->current_buffer_size = 0;
442   }
443
444   GST_DEBUG_OBJECT (sink, "opened file %s, seekable %d",
445       sink->filename, sink->seekable);
446
447   return TRUE;
448
449   /* ERRORS */
450 no_filename:
451   {
452     GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
453         (_("No file name specified for writing.")), (NULL));
454     return FALSE;
455   }
456 open_failed:
457   {
458     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
459         (_("Could not open file \"%s\" for writing."), sink->filename),
460         GST_ERROR_SYSTEM);
461     return FALSE;
462   }
463 }
464
465 static void
466 gst_file_sink_close_file (GstFileSink * sink)
467 {
468   if (sink->file) {
469     if (gst_file_sink_flush_buffer (sink) != GST_FLOW_OK)
470       GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
471           (_("Error closing file \"%s\"."), sink->filename), NULL);
472
473     if (fclose (sink->file) != 0)
474       GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
475           (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
476
477     GST_DEBUG_OBJECT (sink, "closed file");
478     sink->file = NULL;
479   }
480
481   if (sink->buffer) {
482     g_free (sink->buffer);
483     sink->buffer = NULL;
484   }
485   sink->allocated_buffer_size = 0;
486
487   if (sink->buffer_list) {
488     gst_buffer_list_unref (sink->buffer_list);
489     sink->buffer_list = NULL;
490   }
491   sink->current_buffer_size = 0;
492 }
493
494 static gboolean
495 gst_file_sink_query (GstBaseSink * bsink, GstQuery * query)
496 {
497   gboolean res;
498   GstFileSink *self;
499   GstFormat format;
500
501   self = GST_FILE_SINK (bsink);
502
503   switch (GST_QUERY_TYPE (query)) {
504     case GST_QUERY_POSITION:
505       gst_query_parse_position (query, &format, NULL);
506
507       switch (format) {
508         case GST_FORMAT_DEFAULT:
509         case GST_FORMAT_BYTES:
510           gst_query_set_position (query, GST_FORMAT_BYTES,
511               self->current_pos + self->current_buffer_size);
512           res = TRUE;
513           break;
514         default:
515           res = FALSE;
516           break;
517       }
518       break;
519
520     case GST_QUERY_FORMATS:
521       gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
522       res = TRUE;
523       break;
524
525     case GST_QUERY_URI:
526       gst_query_set_uri (query, self->uri);
527       res = TRUE;
528       break;
529
530     case GST_QUERY_SEEKING:
531       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
532       if (format == GST_FORMAT_BYTES || format == GST_FORMAT_DEFAULT) {
533         gst_query_set_seeking (query, GST_FORMAT_BYTES, self->seekable, 0, -1);
534       } else {
535         gst_query_set_seeking (query, format, FALSE, 0, -1);
536       }
537       res = TRUE;
538       break;
539
540     default:
541       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
542       break;
543   }
544   return res;
545 }
546
547 #ifdef HAVE_FSEEKO
548 # define __GST_STDIO_SEEK_FUNCTION "fseeko"
549 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
550 # define __GST_STDIO_SEEK_FUNCTION "lseek"
551 #else
552 # define __GST_STDIO_SEEK_FUNCTION "fseek"
553 #endif
554
555 static gboolean
556 gst_file_sink_do_seek (GstFileSink * filesink, guint64 new_offset)
557 {
558   GST_DEBUG_OBJECT (filesink, "Seeking to offset %" G_GUINT64_FORMAT
559       " using " __GST_STDIO_SEEK_FUNCTION, new_offset);
560
561   if (gst_file_sink_flush_buffer (filesink) != GST_FLOW_OK)
562     goto flush_buffer_failed;
563
564 #ifdef HAVE_FSEEKO
565   if (fseeko (filesink->file, (off_t) new_offset, SEEK_SET) != 0)
566     goto seek_failed;
567 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
568   if (lseek (fileno (filesink->file), (off_t) new_offset,
569           SEEK_SET) == (off_t) - 1)
570     goto seek_failed;
571 #else
572   if (fseek (filesink->file, (long) new_offset, SEEK_SET) != 0)
573     goto seek_failed;
574 #endif
575
576   /* adjust position reporting after seek;
577    * presumably this should basically yield new_offset */
578   gst_file_sink_get_current_offset (filesink, &filesink->current_pos);
579
580   return TRUE;
581
582   /* ERRORS */
583 flush_buffer_failed:
584   {
585     GST_DEBUG_OBJECT (filesink, "Flushing buffer failed");
586     return FALSE;
587   }
588 seek_failed:
589   {
590     GST_DEBUG_OBJECT (filesink, "Seeking failed: %s", g_strerror (errno));
591     return FALSE;
592   }
593 }
594
595 /* handle events (search) */
596 static gboolean
597 gst_file_sink_event (GstBaseSink * sink, GstEvent * event)
598 {
599   GstEventType type;
600   GstFileSink *filesink;
601
602   filesink = GST_FILE_SINK (sink);
603
604   type = GST_EVENT_TYPE (event);
605
606   switch (type) {
607     case GST_EVENT_SEGMENT:
608     {
609       const GstSegment *segment;
610
611       gst_event_parse_segment (event, &segment);
612
613       if (segment->format == GST_FORMAT_BYTES) {
614         /* only try to seek and fail when we are going to a different
615          * position */
616         if (filesink->current_pos + filesink->current_buffer_size !=
617             segment->start) {
618           /* FIXME, the seek should be performed on the pos field, start/stop are
619            * just boundaries for valid bytes offsets. We should also fill the file
620            * with zeroes if the new position extends the current EOF (sparse streams
621            * and segment accumulation). */
622           if (!gst_file_sink_do_seek (filesink, (guint64) segment->start))
623             goto seek_failed;
624         } else {
625           GST_DEBUG_OBJECT (filesink, "Ignored SEGMENT, no seek needed");
626         }
627       } else {
628         GST_DEBUG_OBJECT (filesink,
629             "Ignored SEGMENT event of format %u (%s)", (guint) segment->format,
630             gst_format_get_name (segment->format));
631       }
632       break;
633     }
634     case GST_EVENT_FLUSH_STOP:
635       if (filesink->current_pos != 0 && filesink->seekable) {
636         gst_file_sink_do_seek (filesink, 0);
637         if (ftruncate (fileno (filesink->file), 0))
638           goto truncate_failed;
639       }
640       if (filesink->buffer_list) {
641         gst_buffer_list_unref (filesink->buffer_list);
642         filesink->buffer_list = gst_buffer_list_new ();
643       }
644       filesink->current_buffer_size = 0;
645       break;
646     case GST_EVENT_EOS:
647       if (gst_file_sink_flush_buffer (filesink) != GST_FLOW_OK)
648         goto flush_buffer_failed;
649       break;
650     default:
651       break;
652   }
653
654   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
655
656   /* ERRORS */
657 seek_failed:
658   {
659     GST_ELEMENT_ERROR (filesink, RESOURCE, SEEK,
660         (_("Error while seeking in file \"%s\"."), filesink->filename),
661         GST_ERROR_SYSTEM);
662     gst_event_unref (event);
663     return FALSE;
664   }
665 flush_buffer_failed:
666   {
667     GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
668         (_("Error while writing to file \"%s\"."), filesink->filename), NULL);
669     gst_event_unref (event);
670     return FALSE;
671   }
672 truncate_failed:
673   {
674     GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
675         (_("Error while writing to file \"%s\"."), filesink->filename),
676         GST_ERROR_SYSTEM);
677     gst_event_unref (event);
678     return FALSE;
679   }
680 }
681
682 static gboolean
683 gst_file_sink_get_current_offset (GstFileSink * filesink, guint64 * p_pos)
684 {
685   off_t ret = -1;
686
687   /* no need to flush internal buffer here as this is only called right
688    * after a seek. If this changes then the buffer should be flushed here
689    * too
690    */
691
692 #ifdef HAVE_FTELLO
693   ret = ftello (filesink->file);
694 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
695   ret = lseek (fileno (filesink->file), 0, SEEK_CUR);
696 #else
697   ret = (off_t) ftell (filesink->file);
698 #endif
699
700   if (ret != (off_t) - 1)
701     *p_pos = (guint64) ret;
702
703   return (ret != (off_t) - 1);
704 }
705
706 static GstFlowReturn
707 gst_file_sink_render_list_internal (GstFileSink * sink,
708     GstBufferList * buffer_list)
709 {
710   GstFlowReturn flow;
711   guint num_buffers;
712   guint64 skip = 0;
713
714   num_buffers = gst_buffer_list_length (buffer_list);
715   if (num_buffers == 0)
716     goto no_data;
717
718   GST_DEBUG_OBJECT (sink,
719       "writing %u buffers at position %" G_GUINT64_FORMAT, num_buffers,
720       sink->current_pos);
721
722   for (;;) {
723     guint64 bytes_written = 0;
724
725     flow =
726         gst_writev_buffer_list (GST_OBJECT_CAST (sink), fileno (sink->file),
727         NULL, buffer_list, &bytes_written, skip,
728         sink->max_transient_error_timeout, sink->current_pos, &sink->flushing);
729
730     sink->current_pos += bytes_written;
731     skip += bytes_written;
732
733     if (flow != GST_FLOW_FLUSHING)
734       break;
735
736     flow = gst_base_sink_wait_preroll (GST_BASE_SINK (sink));
737
738     if (flow != GST_FLOW_OK)
739       return flow;
740   }
741
742   return flow;
743
744 no_data:
745   {
746     GST_LOG_OBJECT (sink, "empty buffer list");
747     return GST_FLOW_OK;
748   }
749 }
750
751 static GstFlowReturn
752 gst_file_sink_flush_buffer (GstFileSink * filesink)
753 {
754   GstFlowReturn flow_ret = GST_FLOW_OK;
755
756   GST_DEBUG_OBJECT (filesink, "Flushing out buffer of size %" G_GSIZE_FORMAT,
757       filesink->current_buffer_size);
758
759   if (filesink->buffer && filesink->current_buffer_size) {
760     guint64 skip = 0;
761
762     for (;;) {
763       guint64 bytes_written = 0;
764
765       flow_ret =
766           gst_writev_mem (GST_OBJECT_CAST (filesink), fileno (filesink->file),
767           NULL, filesink->buffer, filesink->current_buffer_size, &bytes_written,
768           skip, filesink->max_transient_error_timeout, filesink->current_pos,
769           &filesink->flushing);
770
771       filesink->current_pos += bytes_written;
772       skip += bytes_written;
773
774       if (flow_ret != GST_FLOW_FLUSHING)
775         break;
776
777       flow_ret = gst_base_sink_wait_preroll (GST_BASE_SINK (filesink));
778       if (flow_ret != GST_FLOW_OK)
779         break;
780     }
781   } else if (filesink->buffer_list && filesink->current_buffer_size) {
782     guint length;
783
784     length = gst_buffer_list_length (filesink->buffer_list);
785
786     if (length > 0) {
787       flow_ret =
788           gst_file_sink_render_list_internal (filesink, filesink->buffer_list);
789       /* Remove all buffers from the list but keep the list. This ensures that
790        * we don't re-allocate the array storing the buffers all the time */
791       gst_buffer_list_remove (filesink->buffer_list, 0, length);
792     }
793   }
794
795   filesink->current_buffer_size = 0;
796
797   return flow_ret;
798 }
799
800 static gboolean
801 has_sync_after_buffer (GstBuffer ** buffer, guint idx, gpointer user_data)
802 {
803   if (GST_BUFFER_FLAG_IS_SET (*buffer, GST_BUFFER_FLAG_SYNC_AFTER)) {
804     gboolean *sync_after = user_data;
805
806     *sync_after = TRUE;
807     return FALSE;
808   }
809
810   return TRUE;
811 }
812
813 static gboolean
814 accumulate_size (GstBuffer ** buffer, guint idx, gpointer user_data)
815 {
816   guint *size = user_data;
817
818   *size += gst_buffer_get_size (*buffer);
819
820   return TRUE;
821 }
822
823 static GstFlowReturn
824 render_buffer (GstFileSink * filesink, GstBuffer * buffer)
825 {
826   GstFlowReturn flow;
827   guint64 bytes_written = 0;
828   guint64 skip = 0;
829
830   for (;;) {
831     flow =
832         gst_writev_buffer (GST_OBJECT_CAST (filesink),
833         fileno (filesink->file), NULL, buffer, &bytes_written, skip,
834         filesink->max_transient_error_timeout, filesink->current_pos,
835         &filesink->flushing);
836
837     filesink->current_pos += bytes_written;
838     skip += bytes_written;
839
840     if (flow != GST_FLOW_FLUSHING)
841       break;
842
843     flow = gst_base_sink_wait_preroll (GST_BASE_SINK (filesink));
844
845     if (flow != GST_FLOW_OK)
846       break;
847   }
848
849   return flow;
850 }
851
852 static GstFlowReturn
853 gst_file_sink_render_list (GstBaseSink * bsink, GstBufferList * buffer_list)
854 {
855   GstFlowReturn flow;
856   GstFileSink *sink;
857   guint i, num_buffers;
858   gboolean sync_after = FALSE;
859   gint fsync_ret;
860
861   sink = GST_FILE_SINK_CAST (bsink);
862
863   num_buffers = gst_buffer_list_length (buffer_list);
864   if (num_buffers == 0)
865     goto no_data;
866
867   gst_buffer_list_foreach (buffer_list, has_sync_after_buffer, &sync_after);
868
869   if (sync_after || (!sink->buffer && !sink->buffer_list)) {
870     flow = gst_file_sink_flush_buffer (sink);
871     if (flow == GST_FLOW_OK)
872       flow = gst_file_sink_render_list_internal (sink, buffer_list);
873   } else {
874     guint size = 0;
875     gst_buffer_list_foreach (buffer_list, accumulate_size, &size);
876
877     GST_DEBUG_OBJECT (sink,
878         "Queueing buffer list of %u bytes (%u buffers) at offset %"
879         G_GUINT64_FORMAT, size, num_buffers,
880         sink->current_pos + sink->current_buffer_size);
881
882     if (sink->buffer) {
883       flow = GST_FLOW_OK;
884       for (i = 0; i < num_buffers && flow == GST_FLOW_OK; i++) {
885         GstBuffer *buffer = gst_buffer_list_get (buffer_list, i);
886         gsize buffer_size = gst_buffer_get_size (buffer);
887
888         if (sink->current_buffer_size + buffer_size >
889             sink->allocated_buffer_size) {
890           flow = gst_file_sink_flush_buffer (sink);
891           if (flow != GST_FLOW_OK)
892             return flow;
893         }
894
895         if (buffer_size > sink->allocated_buffer_size) {
896           GST_DEBUG_OBJECT (sink,
897               "writing buffer ( %" G_GSIZE_FORMAT
898               " bytes) at position %" G_GUINT64_FORMAT,
899               buffer_size, sink->current_pos);
900
901           flow = render_buffer (sink, buffer);
902         } else {
903           sink->current_buffer_size +=
904               gst_buffer_extract (buffer, 0,
905               sink->buffer + sink->current_buffer_size, buffer_size);
906           flow = GST_FLOW_OK;
907         }
908       }
909     } else {
910       for (i = 0; i < num_buffers; ++i)
911         gst_buffer_list_add (sink->buffer_list,
912             gst_buffer_ref (gst_buffer_list_get (buffer_list, i)));
913       sink->current_buffer_size += size;
914
915       if (sink->current_buffer_size > sink->buffer_size)
916         flow = gst_file_sink_flush_buffer (sink);
917       else
918         flow = GST_FLOW_OK;
919     }
920   }
921
922   if (flow == GST_FLOW_OK && sync_after) {
923     do {
924       fsync_ret = fsync (fileno (sink->file));
925     } while (fsync_ret < 0 && errno == EINTR);
926     if (fsync_ret) {
927       GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
928           (_("Error while writing to file \"%s\"."), sink->filename),
929           ("%s", g_strerror (errno)));
930       flow = GST_FLOW_ERROR;
931     }
932   }
933
934   return flow;
935
936 no_data:
937   {
938     GST_LOG_OBJECT (sink, "empty buffer list");
939     return GST_FLOW_OK;
940   }
941 }
942
943 static GstFlowReturn
944 gst_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
945 {
946   GstFileSink *filesink;
947   GstFlowReturn flow;
948   guint8 n_mem;
949   gboolean sync_after;
950   gint fsync_ret;
951
952   filesink = GST_FILE_SINK_CAST (sink);
953
954   sync_after = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_SYNC_AFTER);
955
956   n_mem = gst_buffer_n_memory (buffer);
957
958   if (n_mem > 0 && (sync_after || (!filesink->buffer
959               && !filesink->buffer_list))) {
960     flow = gst_file_sink_flush_buffer (filesink);
961     if (flow == GST_FLOW_OK) {
962       flow = render_buffer (filesink, buffer);
963     }
964   } else if (n_mem > 0) {
965     gsize size = gst_buffer_get_size (buffer);
966
967     GST_DEBUG_OBJECT (filesink,
968         "Queueing buffer of %" G_GSIZE_FORMAT " bytes at offset %"
969         G_GUINT64_FORMAT, size,
970         filesink->current_pos + filesink->current_buffer_size);
971
972     if (filesink->buffer) {
973       if (filesink->current_buffer_size + size >
974           filesink->allocated_buffer_size) {
975         flow = gst_file_sink_flush_buffer (filesink);
976         if (flow != GST_FLOW_OK)
977           return flow;
978       }
979
980       if (size > filesink->allocated_buffer_size) {
981         GST_DEBUG_OBJECT (sink,
982             "writing buffer ( %" G_GSIZE_FORMAT
983             " bytes) at position %" G_GUINT64_FORMAT,
984             size, filesink->current_pos);
985
986         flow = render_buffer (filesink, buffer);
987       } else {
988         filesink->current_buffer_size +=
989             gst_buffer_extract (buffer, 0,
990             filesink->buffer + filesink->current_buffer_size, size);
991         flow = GST_FLOW_OK;
992       }
993     } else {
994       filesink->current_buffer_size += gst_buffer_get_size (buffer);
995       gst_buffer_list_add (filesink->buffer_list, gst_buffer_ref (buffer));
996
997       if (filesink->current_buffer_size > filesink->buffer_size)
998         flow = gst_file_sink_flush_buffer (filesink);
999       else
1000         flow = GST_FLOW_OK;
1001     }
1002   } else {
1003     flow = GST_FLOW_OK;
1004   }
1005
1006   if (flow == GST_FLOW_OK && sync_after) {
1007     do {
1008       fsync_ret = fsync (fileno (filesink->file));
1009     } while (fsync_ret < 0 && errno == EINTR);
1010     if (fsync_ret) {
1011       GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
1012           (_("Error while writing to file \"%s\"."), filesink->filename),
1013           ("%s", g_strerror (errno)));
1014       flow = GST_FLOW_ERROR;
1015     }
1016   }
1017
1018   return flow;
1019 }
1020
1021 static gboolean
1022 gst_file_sink_start (GstBaseSink * basesink)
1023 {
1024   GstFileSink *filesink;
1025
1026   filesink = GST_FILE_SINK_CAST (basesink);
1027
1028   g_atomic_int_set (&filesink->flushing, FALSE);
1029   return gst_file_sink_open_file (filesink);
1030 }
1031
1032 static gboolean
1033 gst_file_sink_stop (GstBaseSink * basesink)
1034 {
1035   GstFileSink *filesink;
1036
1037   filesink = GST_FILE_SINK_CAST (basesink);
1038
1039   gst_file_sink_close_file (filesink);
1040   return TRUE;
1041 }
1042
1043 static gboolean
1044 gst_file_sink_unlock (GstBaseSink * basesink)
1045 {
1046   GstFileSink *filesink;
1047
1048   filesink = GST_FILE_SINK_CAST (basesink);
1049   g_atomic_int_set (&filesink->flushing, TRUE);
1050
1051   return TRUE;
1052 }
1053
1054 static gboolean
1055 gst_file_sink_unlock_stop (GstBaseSink * basesink)
1056 {
1057   GstFileSink *filesink;
1058
1059   filesink = GST_FILE_SINK_CAST (basesink);
1060   g_atomic_int_set (&filesink->flushing, FALSE);
1061
1062   return TRUE;
1063 }
1064
1065 /*** GSTURIHANDLER INTERFACE *************************************************/
1066
1067 static GstURIType
1068 gst_file_sink_uri_get_type (GType type)
1069 {
1070   return GST_URI_SINK;
1071 }
1072
1073 static const gchar *const *
1074 gst_file_sink_uri_get_protocols (GType type)
1075 {
1076   static const gchar *protocols[] = { "file", NULL };
1077
1078   return protocols;
1079 }
1080
1081 static gchar *
1082 gst_file_sink_uri_get_uri (GstURIHandler * handler)
1083 {
1084   GstFileSink *sink = GST_FILE_SINK (handler);
1085
1086   /* FIXME: make thread-safe */
1087   return g_strdup (sink->uri);
1088 }
1089
1090 static gboolean
1091 gst_file_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri,
1092     GError ** error)
1093 {
1094   gchar *location;
1095   gboolean ret;
1096   GstFileSink *sink = GST_FILE_SINK (handler);
1097
1098   /* allow file://localhost/foo/bar by stripping localhost but fail
1099    * for every other hostname */
1100   if (g_str_has_prefix (uri, "file://localhost/")) {
1101     char *tmp;
1102
1103     /* 16 == strlen ("file://localhost") */
1104     tmp = g_strconcat ("file://", uri + 16, NULL);
1105     /* we use gst_uri_get_location() although we already have the
1106      * "location" with uri + 16 because it provides unescaping */
1107     location = gst_uri_get_location (tmp);
1108     g_free (tmp);
1109   } else if (strcmp (uri, "file://") == 0) {
1110     /* Special case for "file://" as this is used by some applications
1111      *  to test with gst_element_make_from_uri if there's an element
1112      *  that supports the URI protocol. */
1113     gst_file_sink_set_location (sink, NULL, NULL);
1114     return TRUE;
1115   } else {
1116     location = gst_uri_get_location (uri);
1117   }
1118
1119   if (!location) {
1120     g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
1121         "File URI without location");
1122     return FALSE;
1123   }
1124
1125   if (!g_path_is_absolute (location)) {
1126     g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
1127         "File URI location must be an absolute path");
1128     g_free (location);
1129     return FALSE;
1130   }
1131
1132   ret = gst_file_sink_set_location (sink, location, error);
1133   g_free (location);
1134
1135   return ret;
1136 }
1137
1138 static void
1139 gst_file_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
1140 {
1141   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1142
1143   iface->get_type = gst_file_sink_uri_get_type;
1144   iface->get_protocols = gst_file_sink_uri_get_protocols;
1145   iface->get_uri = gst_file_sink_uri_get_uri;
1146   iface->set_uri = gst_file_sink_uri_set_uri;
1147 }