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