1 /* GStreamer
2 *
3 * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
4 * Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 /**
23 * SECTION:element-giosrc
24 * @title: giosrc
25 * @see_also: #GstFileSrc, #GstGnomeVFSSrc, #GstGioSink
26 *
27 * This plugin reads data from a local or remote location specified
28 * by an URI. This location can be specified using any protocol supported by
29 * the GIO library or it's VFS backends. Common protocols are 'file', 'http',
30 * 'ftp', or 'smb'.
31 *
32 * If an URI or #GFile is not mounted giosrc will post a message of type
33 * %GST_MESSAGE_ELEMENT with name "not-mounted" on the bus. The message
34 * also contains the #GFile and the corresponding URI.
35 * Applications can use the "not-mounted" message to mount the #GFile
36 * by calling g_file_mount_enclosing_volume() and then restart the
37 * pipeline after the mounting has succeeded. Note that right after the
38 * "not-mounted" message a normal error message is posted on the bus which
39 * should be ignored if "not-mounted" is handled by the application, for
40 * example by calling gst_bus_set_flushing(bus, TRUE) after the "not-mounted"
41 * message was received and gst_bus_set_flushing(bus, FALSE) after the
42 * mounting was successful.
43 *
44 * ## Example launch lines
45 * |[
46 * gst-launch-1.0 -v giosrc location=file:///home/joe/foo.xyz ! fakesink
47 * ]|
48 * The above pipeline will simply read a local file and do nothing with the
49 * data read. Instead of giosrc, we could just as well have used the
50 * filesrc element here.
51 * |[
52 * gst-launch-1.0 -v giosrc location=smb://othercomputer/foo.xyz ! filesink location=/home/joe/foo.xyz
53 * ]|
54 * The above pipeline will copy a file from a remote host to the local file
55 * system using the Samba protocol.
56 * |[
57 * gst-launch-1.0 -v giosrc location=smb://othercomputer/demo.mp3 ! decodebin ! audioconvert ! audioresample ! autoaudiosink
58 * ]|
59 * The above pipeline will read and decode and play an mp3 file from a
60 * SAMBA server.
61 *
62 */
63
64 /* FIXME: We would like to mount the enclosing volume of an URL
65 * if it isn't mounted yet but this is possible async-only.
66 * Unfortunately this requires a running main loop from the
67 * default context and we can't guarantuee this!
68 *
69 * We would also like to do authentication while mounting.
70 */
71
72 #ifdef HAVE_CONFIG_H
73 #include <config.h>
74 #endif
75
76 #include "gstgiosrc.h"
77 #include "gstgioelements.h"
78
79 GST_DEBUG_CATEGORY_STATIC (gst_gio_src_debug);
80 #define GST_CAT_DEFAULT gst_gio_src_debug
81
82 enum
83 {
84 PROP_0,
85 PROP_LOCATION,
86 PROP_FILE,
87 PROP_GROWING_FILE,
88 };
89
90 static gint waiting_data_signal = 0;
91 static gint done_waiting_data_signal = 0;
92
93 #define gst_gio_src_parent_class parent_class
94 G_DEFINE_TYPE_WITH_CODE (GstGioSrc, gst_gio_src,
95 GST_TYPE_GIO_BASE_SRC, gst_gio_uri_handler_do_init (g_define_type_id));
96 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (giosrc, "giosrc",
97 GST_RANK_SECONDARY, GST_TYPE_GIO_SRC, gio_element_init (plugin));
98
99 static void gst_gio_src_finalize (GObject * object);
100
101 static void gst_gio_src_set_property (GObject * object, guint prop_id,
102 const GValue * value, GParamSpec * pspec);
103 static void gst_gio_src_get_property (GObject * object, guint prop_id,
104 GValue * value, GParamSpec * pspec);
105
106 static GInputStream *gst_gio_src_get_stream (GstGioBaseSrc * bsrc);
107
108 static gboolean gst_gio_src_query (GstBaseSrc * base_src, GstQuery * query);
109
110 static gboolean
gst_gio_src_check_deleted(GstGioSrc * src)111 gst_gio_src_check_deleted (GstGioSrc * src)
112 {
113 GstGioBaseSrc *bsrc = GST_GIO_BASE_SRC (src);
114
115 if (!g_file_query_exists (src->file, bsrc->cancel)) {
116 gchar *uri = g_file_get_uri (src->file);
117
118 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
119 ("The underlying file %s is not available anymore", uri));
120
121 g_free (uri);
122
123 return TRUE;
124 }
125
126 return FALSE;
127 }
128
129 static void
gst_gio_src_file_changed_cb(GstGioSrc * src)130 gst_gio_src_file_changed_cb (GstGioSrc * src)
131 {
132 GST_DEBUG_OBJECT (src, "Underlying file changed.");
133 GST_OBJECT_LOCK (src);
134 src->changed = TRUE;
135 if (src->monitoring_mainloop)
136 g_main_loop_quit (src->monitoring_mainloop);
137 GST_OBJECT_UNLOCK (src);
138
139 gst_gio_src_check_deleted (src);
140 }
141
142 static void
gst_gio_src_waited_for_data(GstGioBaseSrc * bsrc)143 gst_gio_src_waited_for_data (GstGioBaseSrc * bsrc)
144 {
145 GstGioSrc *src = GST_GIO_SRC (bsrc);
146
147 src->waiting_for_data = FALSE;
148 g_signal_emit (bsrc, done_waiting_data_signal, 0, NULL);
149 }
150
151 static gboolean
gst_gio_src_wait_for_data(GstGioBaseSrc * bsrc)152 gst_gio_src_wait_for_data (GstGioBaseSrc * bsrc)
153 {
154 GMainContext *ctx;
155 GstGioSrc *src = GST_GIO_SRC (bsrc);
156
157 g_return_val_if_fail (!src->monitor, FALSE);
158
159 if (gst_gio_src_check_deleted (src))
160 return FALSE;
161
162 GST_OBJECT_LOCK (src);
163 if (!src->is_growing) {
164 GST_OBJECT_UNLOCK (src);
165
166 return FALSE;
167 }
168
169 src->monitor = g_file_monitor (src->file, G_FILE_MONITOR_NONE,
170 bsrc->cancel, NULL);
171
172 if (!src->monitor) {
173 GST_OBJECT_UNLOCK (src);
174
175 GST_WARNING_OBJECT (bsrc, "Could not create a monitor");
176 return FALSE;
177 }
178
179 g_signal_connect_swapped (src->monitor, "changed",
180 G_CALLBACK (gst_gio_src_file_changed_cb), src);
181 GST_OBJECT_UNLOCK (src);
182
183 if (!src->waiting_for_data) {
184 g_signal_emit (src, waiting_data_signal, 0, NULL);
185 src->waiting_for_data = TRUE;
186 }
187
188 ctx = g_main_context_new ();
189 g_main_context_push_thread_default (ctx);
190 GST_OBJECT_LOCK (src);
191 src->changed = FALSE;
192 src->monitoring_mainloop = g_main_loop_new (ctx, FALSE);
193 GST_OBJECT_UNLOCK (src);
194
195 g_main_loop_run (src->monitoring_mainloop);
196
197 g_signal_handlers_disconnect_by_func (src->monitor,
198 gst_gio_src_file_changed_cb, src);
199
200 GST_OBJECT_LOCK (src);
201 gst_clear_object (&src->monitor);
202 g_main_loop_unref (src->monitoring_mainloop);
203 src->monitoring_mainloop = NULL;
204 GST_OBJECT_UNLOCK (src);
205
206 g_main_context_pop_thread_default (ctx);
207 g_main_context_unref (ctx);
208
209 return src->changed;
210 }
211
212 static gboolean
gst_gio_src_unlock(GstBaseSrc * base_src)213 gst_gio_src_unlock (GstBaseSrc * base_src)
214 {
215 GstGioSrc *src = GST_GIO_SRC (base_src);
216
217 GST_LOG_OBJECT (src, "triggering cancellation");
218
219 GST_OBJECT_LOCK (src);
220 while (src->monitoring_mainloop) {
221 /* Ensure that we have already started the mainloop */
222 if (!g_main_loop_is_running (src->monitoring_mainloop)) {
223 GST_OBJECT_UNLOCK (src);
224
225 /* Letting a chance for the waiting for data function to cleanup the
226 * mainloop. */
227 g_thread_yield ();
228
229 GST_OBJECT_LOCK (src);
230 continue;
231 }
232 g_main_loop_quit (src->monitoring_mainloop);
233 break;
234 }
235 GST_OBJECT_UNLOCK (src);
236
237 return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS, unlock, (base_src),
238 TRUE);
239 }
240
241 static void
gst_gio_src_class_init(GstGioSrcClass * klass)242 gst_gio_src_class_init (GstGioSrcClass * klass)
243 {
244 GObjectClass *gobject_class = (GObjectClass *) klass;
245 GstElementClass *gstelement_class = (GstElementClass *) klass;
246 GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;
247 GstGioBaseSrcClass *gstgiobasesrc_class = (GstGioBaseSrcClass *) klass;
248
249 GST_DEBUG_CATEGORY_INIT (gst_gio_src_debug, "gio_src", 0, "GIO source");
250
251 gobject_class->finalize = gst_gio_src_finalize;
252 gobject_class->set_property = gst_gio_src_set_property;
253 gobject_class->get_property = gst_gio_src_get_property;
254
255 g_object_class_install_property (gobject_class, PROP_LOCATION,
256 g_param_spec_string ("location", "Location", "URI location to read from",
257 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
258
259 /**
260 * GstGioSrc:file:
261 *
262 * #GFile to read from.
263 */
264 g_object_class_install_property (gobject_class, PROP_FILE,
265 g_param_spec_object ("file", "File", "GFile to read from",
266 G_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
267
268 /**
269 * GstGioSrc:is-growing:
270 *
271 * Whether the file is currently growing. When activated EOS is never pushed
272 * and the user needs to handle it himself. This modes allows to keep reading
273 * the file while it is being written on file.
274 *
275 * You can reset the property to %FALSE at any time and the file will start
276 * not being considered growing and EOS will be pushed when required.
277 *
278 * Since: 1.20
279 */
280 g_object_class_install_property (gobject_class, PROP_GROWING_FILE,
281 g_param_spec_boolean ("is-growing", "File is growing",
282 "Whether the file is growing, ignoring its end",
283 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
284
285 gst_element_class_set_static_metadata (gstelement_class, "GIO source",
286 "Source/File",
287 "Read from any GIO-supported location",
288 "Ren\xc3\xa9 Stadler <mail@renestadler.de>, "
289 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
290
291 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_gio_src_query);
292 gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_gio_src_unlock);
293
294 gstgiobasesrc_class->get_stream = GST_DEBUG_FUNCPTR (gst_gio_src_get_stream);
295 gstgiobasesrc_class->close_on_stop = TRUE;
296 gstgiobasesrc_class->wait_for_data = gst_gio_src_wait_for_data;
297 gstgiobasesrc_class->waited_for_data = gst_gio_src_waited_for_data;
298
299 /**
300 * GstGioSrc::waiting-data:
301 *
302 * Signal notifying that we are stalled waiting for data
303 *
304 * Since: 1.20
305 */
306 waiting_data_signal = g_signal_new ("waiting-data",
307 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
308 NULL, G_TYPE_NONE, 0);
309
310 /**
311 * GstGioSrc::done-waiting-data:
312 *
313 * Signal notifying that we are done waiting for data
314 *
315 * Since: 1.20
316 */
317 done_waiting_data_signal = g_signal_new ("done-waiting-data",
318 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
319 NULL, G_TYPE_NONE, 0);
320 }
321
322 static void
gst_gio_src_init(GstGioSrc * src)323 gst_gio_src_init (GstGioSrc * src)
324 {
325 }
326
327 static void
gst_gio_src_finalize(GObject * object)328 gst_gio_src_finalize (GObject * object)
329 {
330 GstGioSrc *src = GST_GIO_SRC (object);
331
332 if (src->file) {
333 g_object_unref (src->file);
334 src->file = NULL;
335 }
336
337 GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
338 }
339
340 static void
gst_gio_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)341 gst_gio_src_set_property (GObject * object, guint prop_id,
342 const GValue * value, GParamSpec * pspec)
343 {
344 GstGioSrc *src = GST_GIO_SRC (object);
345
346 switch (prop_id) {
347 case PROP_LOCATION:{
348 const gchar *uri = NULL;
349
350 if (GST_STATE (src) == GST_STATE_PLAYING ||
351 GST_STATE (src) == GST_STATE_PAUSED) {
352 GST_WARNING
353 ("Setting a new location or GFile not supported in PLAYING or PAUSED state");
354 break;
355 }
356
357 GST_OBJECT_LOCK (GST_OBJECT (src));
358 if (src->file)
359 g_object_unref (src->file);
360
361 uri = g_value_get_string (value);
362
363 if (uri) {
364 src->file = g_file_new_for_uri (uri);
365
366 if (!src->file) {
367 GST_ERROR ("Could not create GFile for URI '%s'", uri);
368 }
369 } else {
370 src->file = NULL;
371 }
372 GST_OBJECT_UNLOCK (GST_OBJECT (src));
373 break;
374 }
375 case PROP_GROWING_FILE:
376 {
377 gboolean was_growing;
378
379 GST_OBJECT_LOCK (src);
380 was_growing = src->is_growing;
381 src->is_growing = g_value_get_boolean (value);
382 gst_base_src_set_dynamic_size (GST_BASE_SRC (src), src->is_growing);
383 gst_base_src_set_automatic_eos (GST_BASE_SRC (src), !src->is_growing);
384
385 while (was_growing && !src->is_growing && src->monitoring_mainloop) {
386 /* Ensure that we have already started the mainloop */
387 if (!g_main_loop_is_running (src->monitoring_mainloop)) {
388 GST_OBJECT_UNLOCK (src);
389 /* Letting a chance for the waiting for data function to cleanup the
390 * mainloop. */
391 GST_OBJECT_LOCK (src);
392 continue;
393 }
394 g_main_loop_quit (src->monitoring_mainloop);
395 break;
396 }
397 GST_OBJECT_UNLOCK (src);
398
399 break;
400 }
401 case PROP_FILE:
402 if (GST_STATE (src) == GST_STATE_PLAYING ||
403 GST_STATE (src) == GST_STATE_PAUSED) {
404 GST_WARNING
405 ("Setting a new location or GFile not supported in PLAYING or PAUSED state");
406 break;
407 }
408
409 GST_OBJECT_LOCK (GST_OBJECT (src));
410 if (src->file)
411 g_object_unref (src->file);
412
413 src->file = g_value_dup_object (value);
414
415 GST_OBJECT_UNLOCK (GST_OBJECT (src));
416 break;
417 default:
418 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
419 break;
420 }
421 }
422
423 static void
gst_gio_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)424 gst_gio_src_get_property (GObject * object, guint prop_id,
425 GValue * value, GParamSpec * pspec)
426 {
427 GstGioSrc *src = GST_GIO_SRC (object);
428
429 switch (prop_id) {
430 case PROP_LOCATION:{
431 gchar *uri;
432
433 GST_OBJECT_LOCK (GST_OBJECT (src));
434 if (src->file) {
435 uri = g_file_get_uri (src->file);
436 g_value_set_string (value, uri);
437 g_free (uri);
438 } else {
439 g_value_set_string (value, NULL);
440 }
441 GST_OBJECT_UNLOCK (GST_OBJECT (src));
442 break;
443 }
444 case PROP_FILE:
445 GST_OBJECT_LOCK (GST_OBJECT (src));
446 g_value_set_object (value, src->file);
447 GST_OBJECT_UNLOCK (GST_OBJECT (src));
448 break;
449 case PROP_GROWING_FILE:
450 {
451 g_value_set_boolean (value, src->is_growing);
452 break;
453 }
454 default:
455 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
456 break;
457 }
458 }
459
460 static gboolean
gst_gio_src_query(GstBaseSrc * base_src,GstQuery * query)461 gst_gio_src_query (GstBaseSrc * base_src, GstQuery * query)
462 {
463 gboolean res;
464 GstGioSrc *src = GST_GIO_SRC (base_src);
465
466 switch (GST_QUERY_TYPE (query)) {
467 case GST_QUERY_SCHEDULING:
468 {
469 gchar *scheme;
470 GstSchedulingFlags flags;
471
472 flags = 0;
473 if (src->file == NULL)
474 goto forward_parent;
475
476 scheme = g_file_get_uri_scheme (src->file);
477 if (scheme == NULL)
478 goto forward_parent;
479
480 if (strcmp (scheme, "file") == 0) {
481 GST_LOG_OBJECT (src, "local URI, assuming random access is possible");
482 flags |= GST_SCHEDULING_FLAG_SEEKABLE;
483 } else if (strcmp (scheme, "http") == 0 || strcmp (scheme, "https") == 0) {
484 GST_LOG_OBJECT (src, "blacklisted protocol '%s', "
485 "no random access possible", scheme);
486 } else {
487 GST_LOG_OBJECT (src, "unhandled protocol '%s', asking parent", scheme);
488 g_free (scheme);
489 goto forward_parent;
490 }
491 g_free (scheme);
492
493 gst_query_set_scheduling (query, flags, 1, -1, 0);
494 gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
495 GST_OBJECT_LOCK (src);
496 if (flags & GST_SCHEDULING_FLAG_SEEKABLE && !src->is_growing)
497 gst_query_add_scheduling_mode (query, GST_PAD_MODE_PULL);
498 GST_OBJECT_UNLOCK (src);
499
500 res = TRUE;
501 break;
502 }
503 default:
504 forward_parent:
505 res = GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS,
506 query, (base_src, query), FALSE);
507 break;
508 }
509
510 return res;
511 }
512
513 static GInputStream *
gst_gio_src_get_stream(GstGioBaseSrc * bsrc)514 gst_gio_src_get_stream (GstGioBaseSrc * bsrc)
515 {
516 GstGioSrc *src = GST_GIO_SRC (bsrc);
517 GError *err = NULL;
518 GInputStream *stream;
519 GCancellable *cancel = bsrc->cancel;
520 gchar *uri = NULL;
521
522 if (src->file == NULL) {
523 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
524 ("No location or GFile given"));
525 return NULL;
526 }
527
528 uri = g_file_get_uri (src->file);
529 if (!uri)
530 uri = g_strdup ("(null)");
531
532 stream = G_INPUT_STREAM (g_file_read (src->file, cancel, &err));
533
534 if (stream == NULL && !gst_gio_error (src, "g_file_read", &err, NULL)) {
535 if (GST_GIO_ERROR_MATCHES (err, NOT_FOUND)) {
536 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
537 ("Could not open location %s for reading: %s", uri, err->message));
538 } else if (GST_GIO_ERROR_MATCHES (err, NOT_MOUNTED)) {
539 gst_element_post_message (GST_ELEMENT_CAST (src),
540 gst_message_new_element (GST_OBJECT_CAST (src),
541 gst_structure_new ("not-mounted", "file", G_TYPE_FILE, src->file,
542 "uri", G_TYPE_STRING, uri, NULL)));
543
544 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
545 ("Location %s not mounted: %s", uri, err->message));
546 } else {
547 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
548 ("Could not open location %s for reading: %s", uri, err->message));
549 }
550
551 g_free (uri);
552 g_clear_error (&err);
553 return NULL;
554 } else if (stream == NULL) {
555 g_free (uri);
556 return NULL;
557 }
558
559 GST_DEBUG_OBJECT (src, "opened location %s", uri);
560 g_free (uri);
561
562 return stream;
563 }
564