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