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