|
1 /* GStreamer |
|
2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> |
|
3 * 2000,2005 Wim Taymans <wim@fluendo.com> |
|
4 * |
|
5 * gstfilesrc.c: |
|
6 * |
|
7 * This library is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU Library General Public |
|
9 * License as published by the Free Software Foundation; either |
|
10 * version 2 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This library is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 * Library General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU Library General Public |
|
18 * License along with this library; if not, write to the |
|
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|
20 * Boston, MA 02111-1307, USA. |
|
21 */ |
|
22 /** |
|
23 * SECTION:element-filesrc |
|
24 * @short_description: read from arbitrary point in a file |
|
25 * @see_also: #GstFileSrc |
|
26 * |
|
27 * Read data from a file in the local file system. |
|
28 */ |
|
29 |
|
30 #ifdef HAVE_CONFIG_H |
|
31 # include "config.h" |
|
32 #endif |
|
33 #ifdef __SYMBIAN32__ |
|
34 #include <gst_global.h> |
|
35 #endif |
|
36 |
|
37 #include <gst/gst.h> |
|
38 #include "gstfilesrc.h" |
|
39 |
|
40 #include <stdio.h> |
|
41 #include <sys/types.h> |
|
42 #include <sys/stat.h> |
|
43 #include <fcntl.h> |
|
44 |
|
45 #ifdef __SYMBIAN32__ |
|
46 #include <glib_global.h> |
|
47 #include <gobject_global.h> |
|
48 #endif |
|
49 |
|
50 #ifdef HAVE_UNISTD_H |
|
51 # include <unistd.h> |
|
52 #endif |
|
53 |
|
54 #ifdef HAVE_MMAP |
|
55 # include <sys/mman.h> |
|
56 #endif |
|
57 |
|
58 #ifdef HAVE_WIN32 |
|
59 # include <io.h> /* lseek, open, close, read */ |
|
60 #endif |
|
61 |
|
62 #include <errno.h> |
|
63 #include <string.h> |
|
64 |
|
65 #include "../../gst/gst-i18n-lib.h" |
|
66 #include <gstelement.h> |
|
67 |
|
68 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", |
|
69 GST_PAD_SRC, |
|
70 GST_PAD_ALWAYS, |
|
71 GST_STATIC_CAPS_ANY); |
|
72 |
|
73 /* FIXME we should be using glib for this */ |
|
74 #ifndef S_ISREG |
|
75 #define S_ISREG(mode) ((mode)&_S_IFREG) |
|
76 #endif |
|
77 #ifndef S_ISDIR |
|
78 #define S_ISDIR(mode) ((mode)&_S_IFDIR) |
|
79 #endif |
|
80 #ifndef S_ISSOCK |
|
81 #define S_ISSOCK(x) (0) |
|
82 #endif |
|
83 #ifndef O_BINARY |
|
84 #define O_BINARY (0) |
|
85 #endif |
|
86 |
|
87 |
|
88 /********************************************************************** |
|
89 * GStreamer Default File Source |
|
90 * Theory of Operation |
|
91 * |
|
92 * Update: see GstFileSrc:use-mmap property documentation below |
|
93 * for why use of mmap() is disabled by default. |
|
94 * |
|
95 * This source uses mmap(2) to efficiently load data from a file. |
|
96 * To do this without seriously polluting the applications' memory |
|
97 * space, it must do so in smaller chunks, say 1-4MB at a time. |
|
98 * Buffers are then subdivided from these mmap'd chunks, to directly |
|
99 * make use of the mmap. |
|
100 * |
|
101 * To handle refcounting so that the mmap can be freed at the appropriate |
|
102 * time, a buffer will be created for each mmap'd region, and all new |
|
103 * buffers will be sub-buffers of this top-level buffer. As they are |
|
104 * freed, the refcount goes down on the mmap'd buffer and its free() |
|
105 * function is called, which will call munmap(2) on itself. |
|
106 * |
|
107 * If a buffer happens to cross the boundaries of an mmap'd region, we |
|
108 * have to decide whether it's more efficient to copy the data into a |
|
109 * new buffer, or mmap() just that buffer. There will have to be a |
|
110 * breakpoint size to determine which will be done. The mmap() size |
|
111 * has a lot to do with this as well, because you end up in double- |
|
112 * jeopardy: the larger the outgoing buffer, the more data to copy when |
|
113 * it overlaps, *and* the more frequently you'll have buffers that *do* |
|
114 * overlap. |
|
115 * |
|
116 * Seeking is another tricky aspect to do efficiently. The initial |
|
117 * implementation of this source won't make use of these features, however. |
|
118 * The issue is that if an application seeks backwards in a file, *and* |
|
119 * that region of the file is covered by an mmap that hasn't been fully |
|
120 * deallocated, we really should re-use it. But keeping track of these |
|
121 * regions is tricky because we have to lock the structure that holds |
|
122 * them. We need to settle on a locking primitive (GMutex seems to be |
|
123 * a really good option...), then we can do that. |
|
124 */ |
|
125 |
|
126 |
|
127 GST_DEBUG_CATEGORY_STATIC (gst_file_src_debug); |
|
128 #define GST_CAT_DEFAULT gst_file_src_debug |
|
129 |
|
130 /* FileSrc signals and args */ |
|
131 enum |
|
132 { |
|
133 /* FILL ME */ |
|
134 LAST_SIGNAL |
|
135 }; |
|
136 |
|
137 #define DEFAULT_BLOCKSIZE 4*1024 |
|
138 #define DEFAULT_MMAPSIZE 4*1024*1024 |
|
139 #define DEFAULT_TOUCH TRUE |
|
140 #define DEFAULT_USEMMAP FALSE |
|
141 #define DEFAULT_SEQUENTIAL FALSE |
|
142 |
|
143 enum |
|
144 { |
|
145 ARG_0, |
|
146 ARG_LOCATION, |
|
147 ARG_FD, |
|
148 ARG_MMAPSIZE, |
|
149 ARG_SEQUENTIAL, |
|
150 ARG_TOUCH, |
|
151 ARG_USEMMAP |
|
152 }; |
|
153 |
|
154 static void gst_file_src_finalize (GObject * object); |
|
155 |
|
156 static void gst_file_src_set_property (GObject * object, guint prop_id, |
|
157 const GValue * value, GParamSpec * pspec); |
|
158 static void gst_file_src_get_property (GObject * object, guint prop_id, |
|
159 GValue * value, GParamSpec * pspec); |
|
160 |
|
161 static gboolean gst_file_src_start (GstBaseSrc * basesrc); |
|
162 static gboolean gst_file_src_stop (GstBaseSrc * basesrc); |
|
163 |
|
164 static gboolean gst_file_src_is_seekable (GstBaseSrc * src); |
|
165 static gboolean gst_file_src_get_size (GstBaseSrc * src, guint64 * size); |
|
166 static GstFlowReturn gst_file_src_create (GstBaseSrc * src, guint64 offset, |
|
167 guint length, GstBuffer ** buffer); |
|
168 |
|
169 static void gst_file_src_uri_handler_init (gpointer g_iface, |
|
170 gpointer iface_data); |
|
171 |
|
172 static void |
|
173 _do_init (GType filesrc_type) |
|
174 { |
|
175 static const GInterfaceInfo urihandler_info = { |
|
176 gst_file_src_uri_handler_init, |
|
177 NULL, |
|
178 NULL |
|
179 }; |
|
180 |
|
181 g_type_add_interface_static (filesrc_type, GST_TYPE_URI_HANDLER, |
|
182 &urihandler_info); |
|
183 GST_DEBUG_CATEGORY_INIT (gst_file_src_debug, "filesrc", 0, "filesrc element"); |
|
184 } |
|
185 |
|
186 GST_BOILERPLATE_FULL (GstFileSrc, gst_file_src, GstBaseSrc, GST_TYPE_BASE_SRC, |
|
187 _do_init); |
|
188 |
|
189 static void |
|
190 gst_file_src_base_init (gpointer g_class) |
|
191 { |
|
192 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); |
|
193 |
|
194 gst_element_class_set_details_simple (gstelement_class, |
|
195 "File Source", |
|
196 "Source/File", |
|
197 "Read from arbitrary point in a file", |
|
198 "Erik Walthinsen <omega@cse.ogi.edu>"); |
|
199 gst_element_class_add_pad_template (gstelement_class, |
|
200 gst_static_pad_template_get (&srctemplate)); |
|
201 } |
|
202 |
|
203 static void |
|
204 gst_file_src_class_init (GstFileSrcClass * klass) |
|
205 { |
|
206 GObjectClass *gobject_class; |
|
207 GstElementClass *gstelement_class; |
|
208 GstBaseSrcClass *gstbasesrc_class; |
|
209 |
|
210 gobject_class = G_OBJECT_CLASS (klass); |
|
211 gstelement_class = GST_ELEMENT_CLASS (klass); |
|
212 gstbasesrc_class = GST_BASE_SRC_CLASS (klass); |
|
213 |
|
214 gobject_class->set_property = gst_file_src_set_property; |
|
215 gobject_class->get_property = gst_file_src_get_property; |
|
216 |
|
217 g_object_class_install_property (gobject_class, ARG_FD, |
|
218 g_param_spec_int ("fd", "File-descriptor", |
|
219 "File-descriptor for the file being mmap()d", 0, G_MAXINT, 0, |
|
220 G_PARAM_READABLE)); |
|
221 g_object_class_install_property (gobject_class, ARG_LOCATION, |
|
222 g_param_spec_string ("location", "File Location", |
|
223 "Location of the file to read", NULL, G_PARAM_READWRITE)); |
|
224 g_object_class_install_property (gobject_class, ARG_MMAPSIZE, |
|
225 g_param_spec_ulong ("mmapsize", "mmap() Block Size", |
|
226 "Size in bytes of mmap()d regions", 0, G_MAXULONG, DEFAULT_MMAPSIZE, |
|
227 G_PARAM_READWRITE)); |
|
228 g_object_class_install_property (gobject_class, ARG_TOUCH, |
|
229 g_param_spec_boolean ("touch", "Touch mapped region read data", |
|
230 "Touch mmapped data regions to force them to be read from disk", |
|
231 DEFAULT_TOUCH, G_PARAM_READWRITE)); |
|
232 /** |
|
233 * GstFileSrc:use-mmap |
|
234 * |
|
235 * Whether to use mmap(). Set to TRUE to force use of mmap() instead of |
|
236 * read() for reading data. |
|
237 * |
|
238 * Use of mmap() is disabled by default since with mmap() there are a |
|
239 * number of occasions where the process/application will be notified of |
|
240 * read errors via a SIGBUS signal from the kernel, which will lead to |
|
241 * the application being killed if not handled by the application. This |
|
242 * is something that is difficult to work around for a library like |
|
243 * GStreamer, hence use of mmap() is disabled by default. Said errors |
|
244 * can occur for example when an external device (e.g. an external hard |
|
245 * drive or a portable music player) are unplugged while in use, or when |
|
246 * a CD/DVD medium cannot be be read because the medium is scratched or |
|
247 * otherwise damaged. |
|
248 * |
|
249 **/ |
|
250 g_object_class_install_property (gobject_class, ARG_USEMMAP, |
|
251 g_param_spec_boolean ("use-mmap", "Use mmap to read data", |
|
252 "Whether to use mmap() instead of read()", |
|
253 DEFAULT_USEMMAP, G_PARAM_READWRITE)); |
|
254 g_object_class_install_property (gobject_class, ARG_SEQUENTIAL, |
|
255 g_param_spec_boolean ("sequential", "Optimise for sequential mmap access", |
|
256 "Whether to use madvise to hint to the kernel that access to " |
|
257 "mmap pages will be sequential", |
|
258 DEFAULT_SEQUENTIAL, G_PARAM_READWRITE)); |
|
259 |
|
260 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_file_src_finalize); |
|
261 |
|
262 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_file_src_start); |
|
263 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_file_src_stop); |
|
264 gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_file_src_is_seekable); |
|
265 gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_file_src_get_size); |
|
266 gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_file_src_create); |
|
267 |
|
268 if (sizeof (off_t) < 8) { |
|
269 GST_LOG ("No large file support, sizeof (off_t) = %" G_GSIZE_FORMAT "!", |
|
270 sizeof (off_t)); |
|
271 } |
|
272 } |
|
273 |
|
274 static void |
|
275 gst_file_src_init (GstFileSrc * src, GstFileSrcClass * g_class) |
|
276 { |
|
277 #ifdef HAVE_MMAP |
|
278 src->pagesize = getpagesize (); |
|
279 #endif |
|
280 |
|
281 src->filename = NULL; |
|
282 src->fd = 0; |
|
283 src->uri = NULL; |
|
284 |
|
285 src->touch = DEFAULT_TOUCH; |
|
286 |
|
287 src->mapbuf = NULL; |
|
288 src->mapsize = DEFAULT_MMAPSIZE; /* default is 4MB */ |
|
289 src->use_mmap = DEFAULT_USEMMAP; |
|
290 src->sequential = DEFAULT_SEQUENTIAL; |
|
291 |
|
292 src->is_regular = FALSE; |
|
293 } |
|
294 |
|
295 static void |
|
296 gst_file_src_finalize (GObject * object) |
|
297 { |
|
298 GstFileSrc *src; |
|
299 |
|
300 src = GST_FILE_SRC (object); |
|
301 |
|
302 g_free (src->filename); |
|
303 g_free (src->uri); |
|
304 |
|
305 G_OBJECT_CLASS (parent_class)->finalize (object); |
|
306 } |
|
307 |
|
308 static gboolean |
|
309 gst_file_src_set_location (GstFileSrc * src, const gchar * location) |
|
310 { |
|
311 GstState state; |
|
312 |
|
313 /* the element must be stopped in order to do this */ |
|
314 GST_OBJECT_LOCK (src); |
|
315 state = GST_STATE (src); |
|
316 if (state != GST_STATE_READY && state != GST_STATE_NULL) |
|
317 goto wrong_state; |
|
318 GST_OBJECT_UNLOCK (src); |
|
319 |
|
320 g_free (src->filename); |
|
321 g_free (src->uri); |
|
322 |
|
323 /* clear the filename if we get a NULL (is that possible?) */ |
|
324 if (location == NULL) { |
|
325 src->filename = NULL; |
|
326 src->uri = NULL; |
|
327 } else { |
|
328 src->filename = g_strdup (location); |
|
329 src->uri = gst_uri_construct ("file", src->filename); |
|
330 } |
|
331 g_object_notify (G_OBJECT (src), "location"); |
|
332 gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri); |
|
333 |
|
334 return TRUE; |
|
335 |
|
336 /* ERROR */ |
|
337 wrong_state: |
|
338 { |
|
339 GST_DEBUG_OBJECT (src, "setting location in wrong state"); |
|
340 GST_OBJECT_UNLOCK (src); |
|
341 return FALSE; |
|
342 } |
|
343 } |
|
344 |
|
345 static void |
|
346 gst_file_src_set_property (GObject * object, guint prop_id, |
|
347 const GValue * value, GParamSpec * pspec) |
|
348 { |
|
349 GstFileSrc *src; |
|
350 |
|
351 g_return_if_fail (GST_IS_FILE_SRC (object)); |
|
352 |
|
353 src = GST_FILE_SRC (object); |
|
354 |
|
355 switch (prop_id) { |
|
356 case ARG_LOCATION: |
|
357 gst_file_src_set_location (src, g_value_get_string (value)); |
|
358 break; |
|
359 case ARG_MMAPSIZE: |
|
360 if ((src->mapsize % src->pagesize) == 0) { |
|
361 src->mapsize = g_value_get_ulong (value); |
|
362 } else { |
|
363 GST_INFO_OBJECT (src, |
|
364 "invalid mapsize, must be a multiple of pagesize, which is %d", |
|
365 src->pagesize); |
|
366 } |
|
367 break; |
|
368 case ARG_TOUCH: |
|
369 src->touch = g_value_get_boolean (value); |
|
370 break; |
|
371 case ARG_SEQUENTIAL: |
|
372 src->sequential = g_value_get_boolean (value); |
|
373 break; |
|
374 case ARG_USEMMAP: |
|
375 src->use_mmap = g_value_get_boolean (value); |
|
376 break; |
|
377 default: |
|
378 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
379 break; |
|
380 } |
|
381 } |
|
382 |
|
383 static void |
|
384 gst_file_src_get_property (GObject * object, guint prop_id, GValue * value, |
|
385 GParamSpec * pspec) |
|
386 { |
|
387 GstFileSrc *src; |
|
388 |
|
389 g_return_if_fail (GST_IS_FILE_SRC (object)); |
|
390 |
|
391 src = GST_FILE_SRC (object); |
|
392 |
|
393 switch (prop_id) { |
|
394 case ARG_LOCATION: |
|
395 g_value_set_string (value, src->filename); |
|
396 break; |
|
397 case ARG_FD: |
|
398 g_value_set_int (value, src->fd); |
|
399 break; |
|
400 case ARG_MMAPSIZE: |
|
401 g_value_set_ulong (value, src->mapsize); |
|
402 break; |
|
403 case ARG_TOUCH: |
|
404 g_value_set_boolean (value, src->touch); |
|
405 break; |
|
406 case ARG_SEQUENTIAL: |
|
407 g_value_set_boolean (value, src->sequential); |
|
408 break; |
|
409 case ARG_USEMMAP: |
|
410 g_value_set_boolean (value, src->use_mmap); |
|
411 break; |
|
412 default: |
|
413 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
414 break; |
|
415 } |
|
416 } |
|
417 |
|
418 /*** |
|
419 * mmap code below |
|
420 */ |
|
421 |
|
422 #ifdef HAVE_MMAP |
|
423 |
|
424 /* GstMmapBuffer */ |
|
425 |
|
426 typedef struct _GstMmapBuffer GstMmapBuffer; |
|
427 typedef struct _GstMmapBufferClass GstMmapBufferClass; |
|
428 |
|
429 #define GST_TYPE_MMAP_BUFFER (gst_mmap_buffer_get_type()) |
|
430 |
|
431 #define GST_IS_MMAP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MMAP_BUFFER)) |
|
432 #define GST_IS_MMAP_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MMAP_BUFFER)) |
|
433 #define GST_MMAP_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MMAP_BUFFER, GstMmapBufferClass)) |
|
434 #define GST_MMAP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MMAP_BUFFER, GstMmapBuffer)) |
|
435 #define GST_MMAP_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MMAP_BUFFER, GstMmapBufferClass)) |
|
436 |
|
437 |
|
438 |
|
439 struct _GstMmapBuffer |
|
440 { |
|
441 GstBuffer buffer; |
|
442 |
|
443 GstFileSrc *filesrc; |
|
444 }; |
|
445 |
|
446 struct _GstMmapBufferClass |
|
447 { |
|
448 GstBufferClass buffer_class; |
|
449 }; |
|
450 |
|
451 static void gst_mmap_buffer_init (GTypeInstance * instance, gpointer g_class); |
|
452 static void gst_mmap_buffer_class_init (gpointer g_class, gpointer class_data); |
|
453 static void gst_mmap_buffer_finalize (GstMmapBuffer * mmap_buffer); |
|
454 static GstBufferClass *mmap_buffer_parent_class = NULL; |
|
455 |
|
456 static GType |
|
457 gst_mmap_buffer_get_type (void) |
|
458 { |
|
459 static GType _gst_mmap_buffer_type; |
|
460 |
|
461 if (G_UNLIKELY (_gst_mmap_buffer_type == 0)) { |
|
462 static const GTypeInfo mmap_buffer_info = { |
|
463 sizeof (GstMmapBufferClass), |
|
464 NULL, |
|
465 NULL, |
|
466 gst_mmap_buffer_class_init, |
|
467 NULL, |
|
468 NULL, |
|
469 sizeof (GstMmapBuffer), |
|
470 0, |
|
471 gst_mmap_buffer_init, |
|
472 NULL |
|
473 }; |
|
474 |
|
475 _gst_mmap_buffer_type = g_type_register_static (GST_TYPE_BUFFER, |
|
476 "GstMmapBuffer", &mmap_buffer_info, 0); |
|
477 } |
|
478 return _gst_mmap_buffer_type; |
|
479 } |
|
480 |
|
481 static void |
|
482 gst_mmap_buffer_class_init (gpointer g_class, gpointer class_data) |
|
483 { |
|
484 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); |
|
485 |
|
486 mmap_buffer_parent_class = g_type_class_peek_parent (g_class); |
|
487 |
|
488 mini_object_class->finalize = |
|
489 (GstMiniObjectFinalizeFunction) gst_mmap_buffer_finalize; |
|
490 } |
|
491 |
|
492 static void |
|
493 gst_mmap_buffer_init (GTypeInstance * instance, gpointer g_class) |
|
494 { |
|
495 GstBuffer *buf = (GstBuffer *) instance; |
|
496 |
|
497 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY); |
|
498 /* before we re-enable this flag, we probably need to fix _copy() |
|
499 * _make_writable(), etc. in GstMiniObject/GstBuffer as well */ |
|
500 /* GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_ORIGINAL); */ |
|
501 } |
|
502 |
|
503 static void |
|
504 gst_mmap_buffer_finalize (GstMmapBuffer * mmap_buffer) |
|
505 { |
|
506 guint size; |
|
507 gpointer data; |
|
508 guint64 offset; |
|
509 GstFileSrc *src; |
|
510 GstBuffer *buffer = GST_BUFFER (mmap_buffer); |
|
511 |
|
512 /* get info */ |
|
513 size = GST_BUFFER_SIZE (buffer); |
|
514 offset = GST_BUFFER_OFFSET (buffer); |
|
515 data = GST_BUFFER_DATA (buffer); |
|
516 src = mmap_buffer->filesrc; |
|
517 |
|
518 GST_LOG ("freeing mmap()d buffer at %" G_GUINT64_FORMAT "+%u", offset, size); |
|
519 |
|
520 #ifdef MADV_DONTNEED |
|
521 /* madvise to tell the kernel what to do with it */ |
|
522 if (madvise (data, size, MADV_DONTNEED) < 0) { |
|
523 GST_WARNING_OBJECT (src, "warning: madvise failed: %s", g_strerror (errno)); |
|
524 } |
|
525 #endif |
|
526 |
|
527 /* now unmap the memory */ |
|
528 if (munmap (data, size) < 0) { |
|
529 GST_WARNING_OBJECT (src, "warning: munmap failed: %s", g_strerror (errno)); |
|
530 } |
|
531 |
|
532 /* cast to unsigned long, since there's no gportable way to print |
|
533 * guint64 as hex */ |
|
534 GST_LOG ("unmapped region %08lx+%08lx at %p", |
|
535 (gulong) offset, (gulong) size, data); |
|
536 |
|
537 GST_MINI_OBJECT_CLASS (mmap_buffer_parent_class)-> |
|
538 finalize (GST_MINI_OBJECT (mmap_buffer)); |
|
539 } |
|
540 |
|
541 static GstBuffer * |
|
542 gst_file_src_map_region (GstFileSrc * src, off_t offset, gsize size, |
|
543 gboolean testonly) |
|
544 { |
|
545 GstBuffer *buf; |
|
546 void *mmapregion; |
|
547 |
|
548 g_return_val_if_fail (offset >= 0, NULL); |
|
549 |
|
550 GST_LOG_OBJECT (src, "mapping region %08llx+%08lx from file into memory", |
|
551 offset, (gulong) size); |
|
552 |
|
553 mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset); |
|
554 |
|
555 if (mmapregion == NULL || mmapregion == MAP_FAILED) |
|
556 goto mmap_failed; |
|
557 |
|
558 GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p", |
|
559 (gulong) offset, (gulong) size, mmapregion); |
|
560 |
|
561 /* time to allocate a new mapbuf */ |
|
562 buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_MMAP_BUFFER); |
|
563 /* mmap() the data into this new buffer */ |
|
564 GST_BUFFER_DATA (buf) = mmapregion; |
|
565 GST_MMAP_BUFFER (buf)->filesrc = src; |
|
566 |
|
567 #ifdef MADV_SEQUENTIAL |
|
568 if (src->sequential) { |
|
569 /* madvise to tell the kernel what to do with it */ |
|
570 #ifndef __SYMBIAN32__ |
|
571 if (madvise (mmapregion, size, MADV_SEQUENTIAL) < 0) { |
|
572 GST_WARNING_OBJECT (src, "warning: madvise failed: %s", |
|
573 g_strerror (errno)); |
|
574 } |
|
575 #endif |
|
576 #endif |
|
577 |
|
578 /* fill in the rest of the fields */ |
|
579 GST_BUFFER_SIZE (buf) = size; |
|
580 GST_BUFFER_OFFSET (buf) = offset; |
|
581 GST_BUFFER_OFFSET_END (buf) = offset + size; |
|
582 GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE; |
|
583 |
|
584 return buf; |
|
585 |
|
586 /* ERROR */ |
|
587 mmap_failed: |
|
588 { |
|
589 if (!testonly) { |
|
590 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), |
|
591 ("mmap (0x%08lx, %d, 0x%" G_GINT64_MODIFIER "x) failed: %s", |
|
592 (gulong) size, src->fd, (guint64) offset, g_strerror (errno))); |
|
593 } |
|
594 return NULL; |
|
595 } |
|
596 } |
|
597 |
|
598 static GstBuffer * |
|
599 gst_file_src_map_small_region (GstFileSrc * src, off_t offset, gsize size) |
|
600 { |
|
601 GstBuffer *ret; |
|
602 off_t mod; |
|
603 guint pagesize; |
|
604 |
|
605 GST_LOG_OBJECT (src, |
|
606 "attempting to map a small buffer at %" G_GUINT64_FORMAT "+%d", |
|
607 (guint64) offset, (gint) size); |
|
608 |
|
609 pagesize = src->pagesize; |
|
610 |
|
611 mod = offset % pagesize; |
|
612 |
|
613 /* if the offset starts at a non-page boundary, we have to special case */ |
|
614 if (mod != 0) { |
|
615 gsize mapsize; |
|
616 off_t mapbase; |
|
617 GstBuffer *map; |
|
618 |
|
619 mapbase = offset - mod; |
|
620 mapsize = ((size + mod + pagesize - 1) / pagesize) * pagesize; |
|
621 |
|
622 GST_LOG_OBJECT (src, |
|
623 "not on page boundaries, resizing to map to %" G_GUINT64_FORMAT "+%d", |
|
624 (guint64) mapbase, (gint) mapsize); |
|
625 |
|
626 map = gst_file_src_map_region (src, mapbase, mapsize, FALSE); |
|
627 if (map == NULL) |
|
628 return NULL; |
|
629 |
|
630 ret = gst_buffer_create_sub (map, offset - mapbase, size); |
|
631 GST_BUFFER_OFFSET (ret) = GST_BUFFER_OFFSET (map) + offset - mapbase; |
|
632 |
|
633 gst_buffer_unref (map); |
|
634 } else { |
|
635 ret = gst_file_src_map_region (src, offset, size, FALSE); |
|
636 } |
|
637 |
|
638 return ret; |
|
639 } |
|
640 |
|
641 static GstFlowReturn |
|
642 gst_file_src_create_mmap (GstFileSrc * src, guint64 offset, guint length, |
|
643 GstBuffer ** buffer) |
|
644 { |
|
645 GstBuffer *buf = NULL; |
|
646 gsize readsize, mapsize; |
|
647 off_t readend, mapstart, mapend; |
|
648 int i; |
|
649 |
|
650 /* calculate end pointers so we don't have to do so repeatedly later */ |
|
651 readsize = length; |
|
652 readend = offset + readsize; /* note this is the byte *after* the read */ |
|
653 |
|
654 mapstart = GST_BUFFER_OFFSET (src->mapbuf); |
|
655 mapsize = GST_BUFFER_SIZE (src->mapbuf); |
|
656 mapend = mapstart + mapsize; /* note this is the byte *after* the map */ |
|
657 |
|
658 GST_LOG ("attempting to read %08lx, %08lx, %08lx, %08lx", |
|
659 (unsigned long) readsize, (unsigned long) readend, |
|
660 (unsigned long) mapstart, (unsigned long) mapend); |
|
661 |
|
662 /* if the start is past the mapstart */ |
|
663 if (offset >= mapstart) { |
|
664 /* if the end is before the mapend, the buffer is in current mmap region... */ |
|
665 /* ('cause by definition if readend is in the buffer, so's readstart) */ |
|
666 if (readend <= mapend) { |
|
667 GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%u lives in " |
|
668 "current mapbuf %u+%u, creating subbuffer of mapbuf", |
|
669 offset, (guint) readsize, (guint) mapstart, (guint) mapsize); |
|
670 buf = gst_buffer_create_sub (src->mapbuf, offset - mapstart, readsize); |
|
671 GST_BUFFER_OFFSET (buf) = offset; |
|
672 |
|
673 /* if the start actually is within the current mmap region, map an overlap buffer */ |
|
674 } else if (offset < mapend) { |
|
675 GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%u starts in " |
|
676 "mapbuf %u+%u but ends outside, creating new mmap", |
|
677 offset, (guint) readsize, (guint) mapstart, (guint) mapsize); |
|
678 buf = gst_file_src_map_small_region (src, offset, readsize); |
|
679 if (buf == NULL) |
|
680 goto could_not_mmap; |
|
681 } |
|
682 |
|
683 /* the only other option is that buffer is totally outside, which means we search for it */ |
|
684 |
|
685 /* now we can assume that the start is *before* the current mmap region */ |
|
686 /* if the readend is past mapstart, we have two options */ |
|
687 } else if (readend >= mapstart) { |
|
688 /* either the read buffer overlaps the start of the mmap region */ |
|
689 /* or the read buffer fully contains the current mmap region */ |
|
690 /* either way, it's really not relevant, we just create a new region anyway */ |
|
691 GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d starts before " |
|
692 "mapbuf %d+%d, but overlaps it", (guint64) offset, (gint) readsize, |
|
693 (gint) mapstart, (gint) mapsize); |
|
694 buf = gst_file_src_map_small_region (src, offset, readsize); |
|
695 if (buf == NULL) |
|
696 goto could_not_mmap; |
|
697 } |
|
698 |
|
699 /* then deal with the case where the read buffer is totally outside */ |
|
700 if (buf == NULL) { |
|
701 /* first check to see if there's a map that covers the right region already */ |
|
702 GST_LOG_OBJECT (src, "searching for mapbuf to cover %" G_GUINT64_FORMAT |
|
703 "+%d", offset, (int) readsize); |
|
704 |
|
705 /* if the read buffer crosses a mmap region boundary, create a one-off region */ |
|
706 if ((offset / src->mapsize) != (readend / src->mapsize)) { |
|
707 GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d crosses a " |
|
708 "%d-byte boundary, creating a one-off", offset, (int) readsize, |
|
709 (int) src->mapsize); |
|
710 buf = gst_file_src_map_small_region (src, offset, readsize); |
|
711 if (buf == NULL) |
|
712 goto could_not_mmap; |
|
713 |
|
714 /* otherwise we will create a new mmap region and set it to the default */ |
|
715 } else { |
|
716 gsize mapsize; |
|
717 |
|
718 off_t nextmap = offset - (offset % src->mapsize); |
|
719 |
|
720 GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d in new mapbuf " |
|
721 "at %" G_GUINT64_FORMAT "+%d, mapping and subbuffering", |
|
722 offset, (gint) readsize, (guint64) nextmap, (gint) src->mapsize); |
|
723 /* first, we're done with the old mapbuf */ |
|
724 gst_buffer_unref (src->mapbuf); |
|
725 mapsize = src->mapsize; |
|
726 |
|
727 /* double the mapsize as long as the readsize is smaller */ |
|
728 while (readsize + offset > nextmap + mapsize) { |
|
729 GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d", |
|
730 (guint) readsize, (gint) mapsize); |
|
731 mapsize <<= 1; |
|
732 } |
|
733 /* create a new one */ |
|
734 src->mapbuf = gst_file_src_map_region (src, nextmap, mapsize, FALSE); |
|
735 if (src->mapbuf == NULL) |
|
736 goto could_not_mmap; |
|
737 |
|
738 /* subbuffer it */ |
|
739 buf = gst_buffer_create_sub (src->mapbuf, offset - nextmap, readsize); |
|
740 GST_BUFFER_OFFSET (buf) = |
|
741 GST_BUFFER_OFFSET (src->mapbuf) + offset - nextmap; |
|
742 } |
|
743 } |
|
744 |
|
745 /* if we need to touch the buffer (to bring it into memory), do so */ |
|
746 if (src->touch) { |
|
747 volatile guchar *p = GST_BUFFER_DATA (buf), c; |
|
748 |
|
749 /* read first byte of each page */ |
|
750 for (i = 0; i < GST_BUFFER_SIZE (buf); i += src->pagesize) |
|
751 c = p[i]; |
|
752 } |
|
753 |
|
754 /* we're done, return the buffer */ |
|
755 *buffer = buf; |
|
756 |
|
757 return GST_FLOW_OK; |
|
758 |
|
759 /* ERROR */ |
|
760 could_not_mmap: |
|
761 { |
|
762 return GST_FLOW_ERROR; |
|
763 } |
|
764 } |
|
765 #endif |
|
766 |
|
767 /*** |
|
768 * read code below |
|
769 * that is to say, you shouldn't read the code below, but the code that reads |
|
770 * stuff is below. Well, you shouldn't not read the code below, feel free |
|
771 * to read it of course. It's just that "read code below" is a pretty crappy |
|
772 * documentation string because it sounds like we're expecting you to read |
|
773 * the code to understand what it does, which, while true, is really not |
|
774 * the sort of attitude we want to be advertising. No sir. |
|
775 * |
|
776 */ |
|
777 |
|
778 static GstFlowReturn |
|
779 gst_file_src_create_read (GstFileSrc * src, guint64 offset, guint length, |
|
780 GstBuffer ** buffer) |
|
781 { |
|
782 int ret; |
|
783 GstBuffer *buf; |
|
784 |
|
785 if (G_UNLIKELY (src->read_position != offset)) { |
|
786 off_t res; |
|
787 |
|
788 res = lseek (src->fd, offset, SEEK_SET); |
|
789 if (G_UNLIKELY (res < 0 || res != offset)) |
|
790 goto seek_failed; |
|
791 |
|
792 src->read_position = offset; |
|
793 } |
|
794 |
|
795 buf = gst_buffer_new_and_alloc (length); |
|
796 |
|
797 GST_LOG_OBJECT (src, "Reading %d bytes", length); |
|
798 ret = read (src->fd, GST_BUFFER_DATA (buf), length); |
|
799 if (G_UNLIKELY (ret < 0)) |
|
800 goto could_not_read; |
|
801 |
|
802 /* seekable regular files should have given us what we expected */ |
|
803 if (G_UNLIKELY ((guint) ret < length && src->seekable)) |
|
804 goto unexpected_eos; |
|
805 |
|
806 /* other files should eos if they read 0 and more was requested */ |
|
807 if (G_UNLIKELY (ret == 0 && length > 0)) |
|
808 goto eos; |
|
809 |
|
810 length = ret; |
|
811 |
|
812 GST_BUFFER_SIZE (buf) = length; |
|
813 GST_BUFFER_OFFSET (buf) = offset; |
|
814 GST_BUFFER_OFFSET_END (buf) = offset + length; |
|
815 |
|
816 *buffer = buf; |
|
817 |
|
818 src->read_position += length; |
|
819 |
|
820 return GST_FLOW_OK; |
|
821 |
|
822 /* ERROR */ |
|
823 seek_failed: |
|
824 { |
|
825 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM); |
|
826 return GST_FLOW_ERROR; |
|
827 } |
|
828 could_not_read: |
|
829 { |
|
830 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM); |
|
831 gst_buffer_unref (buf); |
|
832 return GST_FLOW_ERROR; |
|
833 } |
|
834 unexpected_eos: |
|
835 { |
|
836 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), |
|
837 ("unexpected end of file.")); |
|
838 gst_buffer_unref (buf); |
|
839 return GST_FLOW_ERROR; |
|
840 } |
|
841 eos: |
|
842 { |
|
843 GST_DEBUG ("non-regular file hits EOS"); |
|
844 gst_buffer_unref (buf); |
|
845 return GST_FLOW_UNEXPECTED; |
|
846 } |
|
847 } |
|
848 |
|
849 static GstFlowReturn |
|
850 gst_file_src_create (GstBaseSrc * basesrc, guint64 offset, guint length, |
|
851 GstBuffer ** buffer) |
|
852 { |
|
853 GstFileSrc *src; |
|
854 GstFlowReturn ret; |
|
855 |
|
856 src = GST_FILE_SRC (basesrc); |
|
857 |
|
858 #ifdef HAVE_MMAP |
|
859 if (src->using_mmap) { |
|
860 ret = gst_file_src_create_mmap (src, offset, length, buffer); |
|
861 } else { |
|
862 ret = gst_file_src_create_read (src, offset, length, buffer); |
|
863 } |
|
864 #else |
|
865 ret = gst_file_src_create_read (src, offset, length, buffer); |
|
866 #endif |
|
867 |
|
868 return ret; |
|
869 } |
|
870 |
|
871 static gboolean |
|
872 gst_file_src_is_seekable (GstBaseSrc * basesrc) |
|
873 { |
|
874 GstFileSrc *src = GST_FILE_SRC (basesrc); |
|
875 |
|
876 return src->seekable; |
|
877 } |
|
878 |
|
879 static gboolean |
|
880 gst_file_src_get_size (GstBaseSrc * basesrc, guint64 * size) |
|
881 { |
|
882 struct stat stat_results; |
|
883 GstFileSrc *src; |
|
884 |
|
885 src = GST_FILE_SRC (basesrc); |
|
886 |
|
887 if (!src->seekable) { |
|
888 /* If it isn't seekable, we won't know the length (but fstat will still |
|
889 * succeed, and wrongly say our length is zero. */ |
|
890 return FALSE; |
|
891 } |
|
892 |
|
893 if (fstat (src->fd, &stat_results) < 0) |
|
894 goto could_not_stat; |
|
895 |
|
896 *size = stat_results.st_size; |
|
897 |
|
898 return TRUE; |
|
899 |
|
900 /* ERROR */ |
|
901 could_not_stat: |
|
902 { |
|
903 return FALSE; |
|
904 } |
|
905 } |
|
906 |
|
907 /* open the file and mmap it, necessary to go to READY state */ |
|
908 static gboolean |
|
909 gst_file_src_start (GstBaseSrc * basesrc) |
|
910 { |
|
911 GstFileSrc *src = GST_FILE_SRC (basesrc); |
|
912 struct stat stat_results; |
|
913 |
|
914 if (src->filename == NULL || src->filename[0] == '\0') |
|
915 goto no_filename; |
|
916 |
|
917 GST_INFO_OBJECT (src, "opening file %s", src->filename); |
|
918 |
|
919 /* open the file */ |
|
920 src->fd = open (src->filename, O_RDONLY | O_BINARY); |
|
921 if (src->fd < 0) |
|
922 goto open_failed; |
|
923 |
|
924 /* check if it is a regular file, otherwise bail out */ |
|
925 if (fstat (src->fd, &stat_results) < 0) |
|
926 goto no_stat; |
|
927 |
|
928 if (S_ISDIR (stat_results.st_mode)) |
|
929 goto was_directory; |
|
930 |
|
931 if (S_ISSOCK (stat_results.st_mode)) |
|
932 goto was_socket; |
|
933 |
|
934 src->using_mmap = FALSE; |
|
935 src->read_position = 0; |
|
936 |
|
937 /* record if it's a regular (hence seekable and lengthable) file */ |
|
938 if (S_ISREG (stat_results.st_mode)) |
|
939 src->is_regular = TRUE; |
|
940 |
|
941 #ifdef HAVE_MMAP |
|
942 if (src->use_mmap) { |
|
943 /* FIXME: maybe we should only try to mmap if it's a regular file */ |
|
944 /* allocate the first mmap'd region if it's a regular file ? */ |
|
945 src->mapbuf = gst_file_src_map_region (src, 0, src->mapsize, TRUE); |
|
946 if (src->mapbuf != NULL) { |
|
947 GST_DEBUG_OBJECT (src, "using mmap for file"); |
|
948 src->using_mmap = TRUE; |
|
949 src->seekable = TRUE; |
|
950 } |
|
951 } |
|
952 if (src->mapbuf == NULL) |
|
953 #endif |
|
954 { |
|
955 /* If not in mmap mode, we need to check if the underlying file is |
|
956 * seekable. */ |
|
957 off_t res = lseek (src->fd, 0, SEEK_END); |
|
958 |
|
959 if (res < 0) { |
|
960 GST_LOG_OBJECT (src, "disabling seeking, not in mmap mode and lseek " |
|
961 "failed: %s", g_strerror (errno)); |
|
962 src->seekable = FALSE; |
|
963 } else { |
|
964 src->seekable = TRUE; |
|
965 } |
|
966 lseek (src->fd, 0, SEEK_SET); |
|
967 } |
|
968 |
|
969 /* We can only really do seeking on regular files - for other file types, we |
|
970 * don't know their length, so seeking isn't useful/meaningful */ |
|
971 src->seekable = src->seekable && src->is_regular; |
|
972 |
|
973 return TRUE; |
|
974 |
|
975 /* ERROR */ |
|
976 no_filename: |
|
977 { |
|
978 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, |
|
979 (_("No file name specified for reading.")), (NULL)); |
|
980 return FALSE; |
|
981 } |
|
982 open_failed: |
|
983 { |
|
984 switch (errno) { |
|
985 case ENOENT: |
|
986 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), |
|
987 ("No such file \"%s\"", src->filename)); |
|
988 break; |
|
989 default: |
|
990 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, |
|
991 (_("Could not open file \"%s\" for reading."), src->filename), |
|
992 GST_ERROR_SYSTEM); |
|
993 break; |
|
994 } |
|
995 return FALSE; |
|
996 } |
|
997 no_stat: |
|
998 { |
|
999 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, |
|
1000 (_("Could not get info on \"%s\"."), src->filename), (NULL)); |
|
1001 close (src->fd); |
|
1002 return FALSE; |
|
1003 } |
|
1004 was_directory: |
|
1005 { |
|
1006 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, |
|
1007 (_("\"%s\" is a directory."), src->filename), (NULL)); |
|
1008 close (src->fd); |
|
1009 return FALSE; |
|
1010 } |
|
1011 was_socket: |
|
1012 { |
|
1013 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, |
|
1014 (_("File \"%s\" is a socket."), src->filename), (NULL)); |
|
1015 close (src->fd); |
|
1016 return FALSE; |
|
1017 } |
|
1018 } |
|
1019 |
|
1020 /* unmap and close the file */ |
|
1021 static gboolean |
|
1022 gst_file_src_stop (GstBaseSrc * basesrc) |
|
1023 { |
|
1024 GstFileSrc *src = GST_FILE_SRC (basesrc); |
|
1025 |
|
1026 /* close the file */ |
|
1027 close (src->fd); |
|
1028 |
|
1029 /* zero out a lot of our state */ |
|
1030 src->fd = 0; |
|
1031 src->is_regular = FALSE; |
|
1032 |
|
1033 if (src->mapbuf) { |
|
1034 gst_buffer_unref (src->mapbuf); |
|
1035 src->mapbuf = NULL; |
|
1036 } |
|
1037 |
|
1038 return TRUE; |
|
1039 } |
|
1040 |
|
1041 /*** GSTURIHANDLER INTERFACE *************************************************/ |
|
1042 #ifdef __SYMBIAN32__ |
|
1043 GstURIType |
|
1044 #else |
|
1045 static guint |
|
1046 #endif |
|
1047 gst_file_src_uri_get_type (void) |
|
1048 { |
|
1049 return GST_URI_SRC; |
|
1050 } |
|
1051 static gchar ** |
|
1052 gst_file_src_uri_get_protocols (void) |
|
1053 { |
|
1054 static gchar *protocols[] = { "file", NULL }; |
|
1055 |
|
1056 return protocols; |
|
1057 } |
|
1058 static const gchar * |
|
1059 gst_file_src_uri_get_uri (GstURIHandler * handler) |
|
1060 { |
|
1061 GstFileSrc *src = GST_FILE_SRC (handler); |
|
1062 |
|
1063 return src->uri; |
|
1064 } |
|
1065 |
|
1066 static gboolean |
|
1067 gst_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) |
|
1068 { |
|
1069 gchar *protocol, *location; |
|
1070 gboolean ret; |
|
1071 GstFileSrc *src = GST_FILE_SRC (handler); |
|
1072 |
|
1073 protocol = gst_uri_get_protocol (uri); |
|
1074 if (strcmp (protocol, "file") != 0) { |
|
1075 g_free (protocol); |
|
1076 return FALSE; |
|
1077 } |
|
1078 g_free (protocol); |
|
1079 |
|
1080 /* allow file://localhost/foo/bar by stripping localhost but fail |
|
1081 * for every other hostname */ |
|
1082 if (g_str_has_prefix (uri, "file://localhost/")) { |
|
1083 char *tmp; |
|
1084 |
|
1085 /* 16 == strlen ("file://localhost") */ |
|
1086 tmp = g_strconcat ("file://", uri + 16, NULL); |
|
1087 /* we use gst_uri_get_location() although we already have the |
|
1088 * "location" with uri + 16 because it provides unescaping */ |
|
1089 location = gst_uri_get_location (tmp); |
|
1090 g_free (tmp); |
|
1091 } else if (strcmp (uri, "file://") == 0) { |
|
1092 /* Special case for "file://" as this is used by some applications |
|
1093 * to test with gst_element_make_from_uri if there's an element |
|
1094 * that supports the URI protocol. */ |
|
1095 gst_file_src_set_location (src, NULL); |
|
1096 return TRUE; |
|
1097 } else { |
|
1098 location = gst_uri_get_location (uri); |
|
1099 } |
|
1100 |
|
1101 if (!location) |
|
1102 return FALSE; |
|
1103 if (!g_path_is_absolute (location)) { |
|
1104 g_free (location); |
|
1105 return FALSE; |
|
1106 } |
|
1107 |
|
1108 ret = gst_file_src_set_location (src, location); |
|
1109 g_free (location); |
|
1110 |
|
1111 return ret; |
|
1112 } |
|
1113 |
|
1114 static void |
|
1115 gst_file_src_uri_handler_init (gpointer g_iface, gpointer iface_data) |
|
1116 { |
|
1117 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; |
|
1118 |
|
1119 iface->get_type = gst_file_src_uri_get_type; |
|
1120 iface->get_protocols = gst_file_src_uri_get_protocols; |
|
1121 iface->get_uri = gst_file_src_uri_get_uri; |
|
1122 iface->set_uri = gst_file_src_uri_set_uri; |
|
1123 } |