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 <string.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 };
88
89 #define gst_gio_src_parent_class parent_class
90 G_DEFINE_TYPE_WITH_CODE (GstGioSrc, gst_gio_src,
91 GST_TYPE_GIO_BASE_SRC, gst_gio_uri_handler_do_init (g_define_type_id));
92
93 static void gst_gio_src_finalize (GObject * object);
94
95 static void gst_gio_src_set_property (GObject * object, guint prop_id,
96 const GValue * value, GParamSpec * pspec);
97 static void gst_gio_src_get_property (GObject * object, guint prop_id,
98 GValue * value, GParamSpec * pspec);
99
100 static GInputStream *gst_gio_src_get_stream (GstGioBaseSrc * bsrc);
101
102 static gboolean gst_gio_src_query (GstBaseSrc * base_src, GstQuery * query);
103
104 static void
gst_gio_src_class_init(GstGioSrcClass * klass)105 gst_gio_src_class_init (GstGioSrcClass * klass)
106 {
107 GObjectClass *gobject_class = (GObjectClass *) klass;
108 GstElementClass *gstelement_class = (GstElementClass *) klass;
109 GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;
110 GstGioBaseSrcClass *gstgiobasesrc_class = (GstGioBaseSrcClass *) klass;
111
112 GST_DEBUG_CATEGORY_INIT (gst_gio_src_debug, "gio_src", 0, "GIO source");
113
114 gobject_class->finalize = gst_gio_src_finalize;
115 gobject_class->set_property = gst_gio_src_set_property;
116 gobject_class->get_property = gst_gio_src_get_property;
117
118 g_object_class_install_property (gobject_class, PROP_LOCATION,
119 g_param_spec_string ("location", "Location", "URI location to read from",
120 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
121
122 /**
123 * GstGioSrc:file:
124 *
125 * %GFile to read from.
126 */
127 g_object_class_install_property (gobject_class, PROP_FILE,
128 g_param_spec_object ("file", "File", "GFile to read from",
129 G_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
130
131 gst_element_class_set_static_metadata (gstelement_class, "GIO source",
132 "Source/File",
133 "Read from any GIO-supported location",
134 "Ren\xc3\xa9 Stadler <mail@renestadler.de>, "
135 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
136
137 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_gio_src_query);
138
139 gstgiobasesrc_class->get_stream = GST_DEBUG_FUNCPTR (gst_gio_src_get_stream);
140 gstgiobasesrc_class->close_on_stop = TRUE;
141 }
142
143 static void
gst_gio_src_init(GstGioSrc * src)144 gst_gio_src_init (GstGioSrc * src)
145 {
146 }
147
148 static void
gst_gio_src_finalize(GObject * object)149 gst_gio_src_finalize (GObject * object)
150 {
151 GstGioSrc *src = GST_GIO_SRC (object);
152
153 if (src->file) {
154 g_object_unref (src->file);
155 src->file = NULL;
156 }
157
158 GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
159 }
160
161 static void
gst_gio_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)162 gst_gio_src_set_property (GObject * object, guint prop_id,
163 const GValue * value, GParamSpec * pspec)
164 {
165 GstGioSrc *src = GST_GIO_SRC (object);
166
167 switch (prop_id) {
168 case PROP_LOCATION:{
169 const gchar *uri = NULL;
170
171 if (GST_STATE (src) == GST_STATE_PLAYING ||
172 GST_STATE (src) == GST_STATE_PAUSED) {
173 GST_WARNING
174 ("Setting a new location or GFile not supported in PLAYING or PAUSED state");
175 break;
176 }
177
178 GST_OBJECT_LOCK (GST_OBJECT (src));
179 if (src->file)
180 g_object_unref (src->file);
181
182 uri = g_value_get_string (value);
183
184 if (uri) {
185 src->file = g_file_new_for_uri (uri);
186
187 if (!src->file) {
188 GST_ERROR ("Could not create GFile for URI '%s'", uri);
189 }
190 } else {
191 src->file = NULL;
192 }
193 GST_OBJECT_UNLOCK (GST_OBJECT (src));
194 break;
195 }
196 case PROP_FILE:
197 if (GST_STATE (src) == GST_STATE_PLAYING ||
198 GST_STATE (src) == GST_STATE_PAUSED) {
199 GST_WARNING
200 ("Setting a new location or GFile not supported in PLAYING or PAUSED state");
201 break;
202 }
203
204 GST_OBJECT_LOCK (GST_OBJECT (src));
205 if (src->file)
206 g_object_unref (src->file);
207
208 src->file = g_value_dup_object (value);
209
210 GST_OBJECT_UNLOCK (GST_OBJECT (src));
211 break;
212 default:
213 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
214 break;
215 }
216 }
217
218 static void
gst_gio_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)219 gst_gio_src_get_property (GObject * object, guint prop_id,
220 GValue * value, GParamSpec * pspec)
221 {
222 GstGioSrc *src = GST_GIO_SRC (object);
223
224 switch (prop_id) {
225 case PROP_LOCATION:{
226 gchar *uri;
227
228 GST_OBJECT_LOCK (GST_OBJECT (src));
229 if (src->file) {
230 uri = g_file_get_uri (src->file);
231 g_value_set_string (value, uri);
232 g_free (uri);
233 } else {
234 g_value_set_string (value, NULL);
235 }
236 GST_OBJECT_UNLOCK (GST_OBJECT (src));
237 break;
238 }
239 case PROP_FILE:
240 GST_OBJECT_LOCK (GST_OBJECT (src));
241 g_value_set_object (value, src->file);
242 GST_OBJECT_UNLOCK (GST_OBJECT (src));
243 break;
244 default:
245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246 break;
247 }
248 }
249
250 static gboolean
gst_gio_src_query(GstBaseSrc * base_src,GstQuery * query)251 gst_gio_src_query (GstBaseSrc * base_src, GstQuery * query)
252 {
253 gboolean res;
254 GstGioSrc *src = GST_GIO_SRC (base_src);
255
256 switch (GST_QUERY_TYPE (query)) {
257 case GST_QUERY_SCHEDULING:
258 {
259 gchar *scheme;
260 GstSchedulingFlags flags;
261
262 flags = 0;
263 if (src->file == NULL)
264 goto forward_parent;
265
266 scheme = g_file_get_uri_scheme (src->file);
267 if (scheme == NULL)
268 goto forward_parent;
269
270 if (strcmp (scheme, "file") == 0) {
271 GST_LOG_OBJECT (src, "local URI, assuming random access is possible");
272 flags |= GST_SCHEDULING_FLAG_SEEKABLE;
273 } else if (strcmp (scheme, "http") == 0 || strcmp (scheme, "https") == 0) {
274 GST_LOG_OBJECT (src, "blacklisted protocol '%s', "
275 "no random access possible", scheme);
276 } else {
277 GST_LOG_OBJECT (src, "unhandled protocol '%s', asking parent", scheme);
278 goto forward_parent;
279 }
280 g_free (scheme);
281
282 gst_query_set_scheduling (query, flags, 1, -1, 0);
283 gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
284 if (flags & GST_SCHEDULING_FLAG_SEEKABLE)
285 gst_query_add_scheduling_mode (query, GST_PAD_MODE_PULL);
286
287 res = TRUE;
288 break;
289 }
290 default:
291 forward_parent:
292 res = GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS,
293 query, (base_src, query), FALSE);
294 break;
295 }
296
297 return res;
298 }
299
300 static GInputStream *
gst_gio_src_get_stream(GstGioBaseSrc * bsrc)301 gst_gio_src_get_stream (GstGioBaseSrc * bsrc)
302 {
303 GstGioSrc *src = GST_GIO_SRC (bsrc);
304 GError *err = NULL;
305 GInputStream *stream;
306 GCancellable *cancel = bsrc->cancel;
307 gchar *uri = NULL;
308
309 if (src->file == NULL) {
310 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
311 ("No location or GFile given"));
312 return NULL;
313 }
314
315 uri = g_file_get_uri (src->file);
316 if (!uri)
317 uri = g_strdup ("(null)");
318
319 stream = G_INPUT_STREAM (g_file_read (src->file, cancel, &err));
320
321 if (stream == NULL && !gst_gio_error (src, "g_file_read", &err, NULL)) {
322 if (GST_GIO_ERROR_MATCHES (err, NOT_FOUND)) {
323 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
324 ("Could not open location %s for reading: %s", uri, err->message));
325 } else if (GST_GIO_ERROR_MATCHES (err, NOT_MOUNTED)) {
326 gst_element_post_message (GST_ELEMENT_CAST (src),
327 gst_message_new_element (GST_OBJECT_CAST (src),
328 gst_structure_new ("not-mounted", "file", G_TYPE_FILE, src->file,
329 "uri", G_TYPE_STRING, uri, NULL)));
330
331 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
332 ("Location %s not mounted: %s", uri, err->message));
333 } else {
334 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
335 ("Could not open location %s for reading: %s", uri, err->message));
336 }
337
338 g_free (uri);
339 g_clear_error (&err);
340 return NULL;
341 } else if (stream == NULL) {
342 g_free (uri);
343 return NULL;
344 }
345
346 GST_DEBUG_OBJECT (src, "opened location %s", uri);
347 g_free (uri);
348
349 return stream;
350 }
351