2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
4 * 2001 Bastien Nocera <hadess@hadess.net>
5 * 2003 Colin Walters <walters@verbum.org>
6 * 2005 Tim-Philipp Müller <tim centricular net>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
27 * SECTION:element-gnomevfssink
28 * @see_also: #GstFileSink, #GstGnomeVFSSrc
30 * This plugin writes incoming data to a local or remote location specified
31 * by an URI. This location can be specified using any protocol supported by
32 * the GnomeVFS library. Common protocols are 'file', 'ftp', or 'smb'.
34 * Applications can connect to the #GstGnomeVFSSink::allow-overwrite signal to
35 * receive a callback when an existing file will be overwritten. The return
36 * value of the signal will determine if gnomevfssink will overwrite the
37 * resource or abort with an error.
40 * <title>Example launch lines</title>
42 * gst-launch -v filesrc location=input.xyz ! gnomevfssink location=file:///home/joe/out.xyz
43 * ]| The above pipeline will simply copy a local file. Instead of gnomevfssink,
44 * we could just as well have used the filesink element here.
46 * gst-launch -v filesrc location=foo.mp3 ! mad ! flacenc ! gnomevfssink location=smb://othercomputer/foo.flac
47 * ]| The above pipeline will re-encode an mp3 file into FLAC format and store
48 * it on a remote host using the Samba protocol.
51 * Last reviewed on 2006-02-28 (0.10.4)
58 #include "gstgnomevfssink.h"
60 #include "gst/gst-i18n-plugin.h"
63 #include <libgnomevfs/gnome-vfs.h>
81 static void gst_gnome_vfs_sink_finalize (GObject * obj);
83 static void gst_gnome_vfs_sink_uri_handler_init (gpointer g_iface,
86 static void gst_gnome_vfs_sink_set_property (GObject * object, guint prop_id,
87 const GValue * value, GParamSpec * pspec);
88 static void gst_gnome_vfs_sink_get_property (GObject * object, guint prop_id,
89 GValue * value, GParamSpec * pspec);
91 static gboolean gst_gnome_vfs_sink_open_file (GstGnomeVFSSink * sink);
92 static void gst_gnome_vfs_sink_close_file (GstGnomeVFSSink * sink);
93 static gboolean gst_gnome_vfs_sink_start (GstBaseSink * basesink);
94 static gboolean gst_gnome_vfs_sink_stop (GstBaseSink * basesink);
95 static GstFlowReturn gst_gnome_vfs_sink_render (GstBaseSink * basesink,
97 static gboolean gst_gnome_vfs_sink_handle_event (GstBaseSink * basesink,
99 static gboolean gst_gnome_vfs_sink_query (GstPad * pad, GstQuery * query);
101 static guint gst_gnome_vfs_sink_signals[LAST_SIGNAL]; /* all 0 */
103 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
106 GST_STATIC_CAPS_ANY);
108 GST_DEBUG_CATEGORY_STATIC (gst_gnome_vfs_sink_debug);
109 #define GST_CAT_DEFAULT gst_gnome_vfs_sink_debug
112 gst_gnome_vfs_sink_do_init (GType type)
114 static const GInterfaceInfo urihandler_info = {
115 gst_gnome_vfs_sink_uri_handler_init,
120 g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
122 GST_DEBUG_CATEGORY_INIT (gst_gnome_vfs_sink_debug, "gnomevfssink", 0,
123 "Gnome VFS sink element");
126 GST_BOILERPLATE_FULL (GstGnomeVFSSink, gst_gnome_vfs_sink, GstBaseSink,
127 GST_TYPE_BASE_SINK, gst_gnome_vfs_sink_do_init);
130 gst_gnome_vfs_sink_base_init (gpointer g_class)
132 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
134 gst_element_class_add_pad_template (element_class,
135 gst_static_pad_template_get (&sinktemplate));
137 gst_element_class_set_details_simple (element_class,
138 "GnomeVFS Sink", "Sink/File",
139 "Write a stream to a GnomeVFS URI", "Bastien Nocera <hadess@hadess.net>");
143 _gst_boolean_allow_overwrite_accumulator (GSignalInvocationHint * ihint,
144 GValue * return_accu, const GValue * handler_return, gpointer dummy)
146 gboolean allow_overwrite;
148 allow_overwrite = g_value_get_boolean (handler_return);
149 if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
150 g_value_set_boolean (return_accu, allow_overwrite);
152 /* stop emission if signal doesn't allow overwriting */
153 return allow_overwrite;
157 gst_gnome_vfs_sink_class_init (GstGnomeVFSSinkClass * klass)
159 GstBaseSinkClass *basesink_class;
160 GObjectClass *gobject_class;
162 gobject_class = (GObjectClass *) klass;
163 basesink_class = (GstBaseSinkClass *) klass;
165 gobject_class->set_property = gst_gnome_vfs_sink_set_property;
166 gobject_class->get_property = gst_gnome_vfs_sink_get_property;
167 gobject_class->finalize = gst_gnome_vfs_sink_finalize;
169 g_object_class_install_property (gobject_class, ARG_LOCATION,
170 g_param_spec_string ("location", "File Location",
171 "Location of the file to write", NULL,
172 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
173 g_object_class_install_property (gobject_class, ARG_URI,
174 g_param_spec_boxed ("uri", "GnomeVFSURI", "URI for GnomeVFS",
175 GST_TYPE_GNOME_VFS_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
176 g_object_class_install_property (gobject_class, ARG_HANDLE,
177 g_param_spec_boxed ("handle", "GnomeVFSHandle", "Handle for GnomeVFS",
178 GST_TYPE_GNOME_VFS_HANDLE,
179 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
182 * GstGnomeVFSSink::allow-overwrite
183 * @sink: the object which received the signal
184 * @uri: the URI to be overwritten
186 * This signal is fired when gnomevfssink is about to overwrite an
187 * existing resource. The application can connect to this signal and ask
188 * the user if the resource may be overwritten.
190 * Returns: A boolean indicating that the resource may be overwritten.
192 gst_gnome_vfs_sink_signals[SIGNAL_ERASE_ASK] =
193 g_signal_new ("allow-overwrite", G_TYPE_FROM_CLASS (klass),
194 G_SIGNAL_RUN_CLEANUP, G_STRUCT_OFFSET (GstGnomeVFSSinkClass, erase_ask),
195 _gst_boolean_allow_overwrite_accumulator, NULL,
196 gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, GST_TYPE_GNOME_VFS_URI);
198 basesink_class->stop = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_stop);
199 basesink_class->start = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_start);
200 basesink_class->event = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_handle_event);
201 basesink_class->render = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_render);
202 basesink_class->get_times = NULL;
206 gst_gnome_vfs_sink_finalize (GObject * obj)
208 GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (obj);
211 gnome_vfs_uri_unref (sink->uri);
215 if (sink->uri_name) {
216 g_free (sink->uri_name);
217 sink->uri_name = NULL;
220 G_OBJECT_CLASS (parent_class)->finalize (obj);
224 gst_gnome_vfs_sink_init (GstGnomeVFSSink * sink, GstGnomeVFSSinkClass * klass)
226 gst_pad_set_query_function (GST_BASE_SINK_PAD (sink),
227 GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_query));
230 sink->uri_name = NULL;
232 sink->own_handle = FALSE;
233 sink->current_pos = 0;
235 GST_BASE_SINK (sink)->sync = FALSE;
239 gst_gnome_vfs_sink_set_property (GObject * object, guint prop_id,
240 const GValue * value, GParamSpec * pspec)
242 GstGnomeVFSSink *sink;
245 sink = GST_GNOME_VFS_SINK (object);
247 gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0);
249 if (cur_state == GST_STATE_PLAYING || cur_state == GST_STATE_PAUSED) {
250 GST_WARNING_OBJECT (sink, "cannot set property when PAUSED or PLAYING");
254 GST_OBJECT_LOCK (sink);
258 const gchar *new_location;
261 gnome_vfs_uri_unref (sink->uri);
264 if (sink->uri_name) {
265 g_free (sink->uri_name);
266 sink->uri_name = NULL;
269 new_location = g_value_get_string (value);
271 sink->uri_name = gst_gnome_vfs_location_to_uri_string (new_location);
272 sink->uri = gnome_vfs_uri_new (sink->uri_name);
278 gnome_vfs_uri_unref (sink->uri);
281 if (sink->uri_name) {
282 g_free (sink->uri_name);
283 sink->uri_name = NULL;
285 if (g_value_get_boxed (value)) {
286 sink->uri = (GnomeVFSURI *) g_value_dup_boxed (value);
287 sink->uri_name = gnome_vfs_uri_to_string (sink->uri, 0);
293 gnome_vfs_uri_unref (sink->uri);
296 if (sink->uri_name) {
297 g_free (sink->uri_name);
298 sink->uri_name = NULL;
300 sink->handle = g_value_get_boxed (value);
304 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
308 GST_OBJECT_UNLOCK (sink);
312 gst_gnome_vfs_sink_get_property (GObject * object, guint prop_id,
313 GValue * value, GParamSpec * pspec)
315 GstGnomeVFSSink *sink;
317 sink = GST_GNOME_VFS_SINK (object);
319 GST_OBJECT_LOCK (sink);
323 g_value_set_string (value, sink->uri_name);
326 g_value_set_boxed (value, sink->uri);
329 g_value_set_boxed (value, sink->handle);
332 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
336 GST_OBJECT_UNLOCK (sink);
340 gst_gnome_vfs_sink_open_file (GstGnomeVFSSink * sink)
342 GnomeVFSResult result;
345 /* open the file, all permissions, umask will apply */
346 result = gnome_vfs_create_uri (&(sink->handle), sink->uri,
347 GNOME_VFS_OPEN_WRITE, TRUE,
348 GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE |
349 GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE |
350 GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE);
352 /* if the file existed and the property says to ask, then ask! */
353 if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
354 gboolean erase_anyway = FALSE;
356 g_signal_emit (G_OBJECT (sink),
357 gst_gnome_vfs_sink_signals[SIGNAL_ERASE_ASK], 0, sink->uri,
360 result = gnome_vfs_create_uri (&(sink->handle), sink->uri,
361 GNOME_VFS_OPEN_WRITE, FALSE,
362 GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE |
363 GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE |
364 GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE);
368 GST_DEBUG_OBJECT (sink, "open: %s", gnome_vfs_result_to_string (result));
370 if (result != GNOME_VFS_OK) {
371 gchar *filename = gnome_vfs_uri_to_string (sink->uri,
372 GNOME_VFS_URI_HIDE_PASSWORD);
374 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
375 (_("Could not open vfs file \"%s\" for writing: %s."),
376 filename, gnome_vfs_result_to_string (result)), GST_ERROR_SYSTEM);
380 sink->own_handle = TRUE;
381 } else if (!sink->handle) {
382 GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (_("No filename given")),
386 sink->own_handle = FALSE;
389 sink->current_pos = 0;
395 gst_gnome_vfs_sink_close_file (GstGnomeVFSSink * sink)
397 GnomeVFSResult result;
399 if (sink->own_handle) {
401 result = gnome_vfs_close (sink->handle);
403 if (result != GNOME_VFS_OK) {
404 gchar *filename = gnome_vfs_uri_to_string (sink->uri,
405 GNOME_VFS_URI_HIDE_PASSWORD);
407 GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
408 (_("Could not close vfs file \"%s\"."), filename), GST_ERROR_SYSTEM);
412 sink->own_handle = FALSE;
418 gst_gnome_vfs_sink_start (GstBaseSink * basesink)
422 ret = gst_gnome_vfs_sink_open_file (GST_GNOME_VFS_SINK (basesink));
428 gst_gnome_vfs_sink_stop (GstBaseSink * basesink)
430 GST_DEBUG_OBJECT (basesink, "closing ...");
431 gst_gnome_vfs_sink_close_file (GST_GNOME_VFS_SINK (basesink));
436 gst_gnome_vfs_sink_handle_event (GstBaseSink * basesink, GstEvent * event)
438 GstGnomeVFSSink *sink;
441 sink = GST_GNOME_VFS_SINK (basesink);
443 GST_DEBUG_OBJECT (sink, "processing %s event", GST_EVENT_TYPE_NAME (event));
445 switch (GST_EVENT_TYPE (event)) {
446 case GST_EVENT_NEWSEGMENT:{
451 gst_event_parse_new_segment (event, NULL, NULL, &format, &offset,
454 if (format != GST_FORMAT_BYTES) {
455 GST_WARNING_OBJECT (sink, "ignored NEWSEGMENT event in %s format",
456 gst_format_get_name (format));
460 GST_LOG_OBJECT (sink, "seeking to offset %" G_GINT64_FORMAT, offset);
461 res = gnome_vfs_seek (sink->handle, GNOME_VFS_SEEK_START, offset);
463 if (res != GNOME_VFS_OK) {
464 GST_ERROR_OBJECT (sink, "Failed to seek to offset %"
465 G_GINT64_FORMAT ": %s", offset, gnome_vfs_result_to_string (res));
468 sink->current_pos = offset;
474 case GST_EVENT_FLUSH_START:
476 /* No need to flush with GnomeVfs */
487 gst_gnome_vfs_sink_query (GstPad * pad, GstQuery * query)
489 GstGnomeVFSSink *sink;
492 sink = GST_GNOME_VFS_SINK (GST_PAD_PARENT (pad));
494 switch (GST_QUERY_TYPE (query)) {
495 case GST_QUERY_POSITION:
496 gst_query_parse_position (query, &format, NULL);
498 case GST_FORMAT_DEFAULT:
499 case GST_FORMAT_BYTES:
500 gst_query_set_position (query, GST_FORMAT_BYTES, sink->current_pos);
506 case GST_QUERY_FORMATS:
507 gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
511 gst_query_set_uri (query, sink->uri_name);
515 return gst_pad_query_default (pad, query);
520 gst_gnome_vfs_sink_render (GstBaseSink * basesink, GstBuffer * buf)
522 GnomeVFSFileSize written, cur_pos;
523 GstGnomeVFSSink *sink;
524 GnomeVFSResult result;
527 sink = GST_GNOME_VFS_SINK (basesink);
529 if (gnome_vfs_tell (sink->handle, &cur_pos) == GNOME_VFS_OK) {
530 /* bring up to date with current position for proper reporting */
531 sink->current_pos = cur_pos;
534 result = gnome_vfs_write (sink->handle, GST_BUFFER_DATA (buf),
535 GST_BUFFER_SIZE (buf), &written);
539 GST_DEBUG_OBJECT (sink, "wrote %" G_GINT64_FORMAT " bytes at %"
540 G_GINT64_FORMAT, (gint64) written, (gint64) cur_pos);
542 if (written < GST_BUFFER_SIZE (buf)) {
543 /* FIXME: what to do here? (tpm) */
544 g_warning ("%s: %d bytes should be written, only %"
545 G_GUINT64_FORMAT " bytes written", G_STRLOC,
546 GST_BUFFER_SIZE (buf), written);
549 sink->current_pos += GST_BUFFER_SIZE (buf);
553 case GNOME_VFS_ERROR_NO_SPACE:{
554 /* TODO: emit signal/send msg on out-of-diskspace and
555 * handle this gracefully (see open bug) (tpm) */
556 GST_ELEMENT_ERROR (sink, RESOURCE, NO_SPACE_LEFT, (NULL),
557 ("bufsize=%u, written=%u", GST_BUFFER_SIZE (buf), (guint) written));
558 ret = GST_FLOW_ERROR;
562 gchar *filename = gnome_vfs_uri_to_string (sink->uri,
563 GNOME_VFS_URI_HIDE_PASSWORD);
565 GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
566 (_("Error while writing to file \"%s\"."), filename),
567 ("%s, bufsize=%u, written=%u", gnome_vfs_result_to_string (result),
568 GST_BUFFER_SIZE (buf), (guint) written));
571 ret = GST_FLOW_ERROR;
579 /*** GSTURIHANDLER INTERFACE *************************************************/
582 gst_gnome_vfs_sink_uri_get_type (void)
588 gst_gnome_vfs_sink_uri_get_protocols (void)
590 return gst_gnomevfs_get_supported_uris ();
594 gst_gnome_vfs_sink_uri_get_uri (GstURIHandler * handler)
596 GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (handler);
598 return sink->uri_name;
602 gst_gnome_vfs_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
604 GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (handler);
607 gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0);
609 if (cur_state == GST_STATE_PLAYING || cur_state == GST_STATE_PAUSED) {
610 GST_WARNING_OBJECT (sink, "cannot set uri when PAUSED or PLAYING");
614 g_object_set (sink, "location", uri, NULL);
620 gst_gnome_vfs_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
622 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
624 iface->get_type = gst_gnome_vfs_sink_uri_get_type;
625 iface->get_protocols = gst_gnome_vfs_sink_uri_get_protocols;
626 iface->get_uri = gst_gnome_vfs_sink_uri_get_uri;
627 iface->set_uri = gst_gnome_vfs_sink_uri_set_uri;