1 /* GStreamer |
|
2 * |
|
3 * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> |
|
4 * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> |
|
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., 59 Temple Place - Suite 330, |
|
19 * Boston, MA 02111-1307, USA. |
|
20 */ |
|
21 |
|
22 #ifdef HAVE_CONFIG_H |
|
23 #include <config.h> |
|
24 #endif |
|
25 |
|
26 #include "gstgiobasesrc.h" |
|
27 |
|
28 GST_DEBUG_CATEGORY_STATIC (gst_gio_base_src_debug); |
|
29 #define GST_CAT_DEFAULT gst_gio_base_src_debug |
|
30 |
|
31 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", |
|
32 GST_PAD_SRC, |
|
33 GST_PAD_ALWAYS, |
|
34 GST_STATIC_CAPS_ANY); |
|
35 |
|
36 GST_BOILERPLATE (GstGioBaseSrc, gst_gio_base_src, GstBaseSrc, |
|
37 GST_TYPE_BASE_SRC); |
|
38 |
|
39 static void gst_gio_base_src_finalize (GObject * object); |
|
40 static gboolean gst_gio_base_src_start (GstBaseSrc * base_src); |
|
41 static gboolean gst_gio_base_src_stop (GstBaseSrc * base_src); |
|
42 static gboolean gst_gio_base_src_get_size (GstBaseSrc * base_src, |
|
43 guint64 * size); |
|
44 static gboolean gst_gio_base_src_is_seekable (GstBaseSrc * base_src); |
|
45 static gboolean gst_gio_base_src_unlock (GstBaseSrc * base_src); |
|
46 static gboolean gst_gio_base_src_unlock_stop (GstBaseSrc * base_src); |
|
47 static gboolean gst_gio_base_src_check_get_range (GstBaseSrc * base_src); |
|
48 static GstFlowReturn gst_gio_base_src_create (GstBaseSrc * base_src, |
|
49 guint64 offset, guint size, GstBuffer ** buf); |
|
50 |
|
51 static void |
|
52 gst_gio_base_src_base_init (gpointer gclass) |
|
53 { |
|
54 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); |
|
55 |
|
56 GST_DEBUG_CATEGORY_INIT (gst_gio_base_src_debug, "gio_base_src", 0, |
|
57 "GIO base source"); |
|
58 |
|
59 gst_element_class_add_pad_template (element_class, |
|
60 gst_static_pad_template_get (&src_factory)); |
|
61 } |
|
62 |
|
63 static void |
|
64 gst_gio_base_src_class_init (GstGioBaseSrcClass * klass) |
|
65 { |
|
66 GObjectClass *gobject_class; |
|
67 GstElementClass *gstelement_class; |
|
68 GstBaseSrcClass *gstbasesrc_class; |
|
69 |
|
70 gobject_class = (GObjectClass *) klass; |
|
71 gstelement_class = (GstElementClass *) klass; |
|
72 gstbasesrc_class = (GstBaseSrcClass *) klass; |
|
73 |
|
74 gobject_class->finalize = gst_gio_base_src_finalize; |
|
75 |
|
76 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gio_base_src_start); |
|
77 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gio_base_src_stop); |
|
78 gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gio_base_src_get_size); |
|
79 gstbasesrc_class->is_seekable = |
|
80 GST_DEBUG_FUNCPTR (gst_gio_base_src_is_seekable); |
|
81 gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_gio_base_src_unlock); |
|
82 gstbasesrc_class->unlock_stop = |
|
83 GST_DEBUG_FUNCPTR (gst_gio_base_src_unlock_stop); |
|
84 gstbasesrc_class->check_get_range = |
|
85 GST_DEBUG_FUNCPTR (gst_gio_base_src_check_get_range); |
|
86 gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gio_base_src_create); |
|
87 } |
|
88 |
|
89 static void |
|
90 gst_gio_base_src_init (GstGioBaseSrc * src, GstGioBaseSrcClass * gclass) |
|
91 { |
|
92 src->cancel = g_cancellable_new (); |
|
93 } |
|
94 |
|
95 static void |
|
96 gst_gio_base_src_finalize (GObject * object) |
|
97 { |
|
98 GstGioBaseSrc *src = GST_GIO_BASE_SRC (object); |
|
99 |
|
100 if (src->cancel) { |
|
101 g_object_unref (src->cancel); |
|
102 src->cancel = NULL; |
|
103 } |
|
104 |
|
105 if (src->stream) { |
|
106 g_object_unref (src->stream); |
|
107 src->stream = NULL; |
|
108 } |
|
109 |
|
110 GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); |
|
111 } |
|
112 |
|
113 static gboolean |
|
114 gst_gio_base_src_start (GstBaseSrc * base_src) |
|
115 { |
|
116 GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); |
|
117 |
|
118 if (!G_IS_INPUT_STREAM (src->stream)) { |
|
119 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), |
|
120 ("No stream given yet")); |
|
121 return FALSE; |
|
122 } |
|
123 |
|
124 src->position = 0; |
|
125 |
|
126 GST_DEBUG_OBJECT (src, "started stream"); |
|
127 |
|
128 return TRUE; |
|
129 } |
|
130 |
|
131 static gboolean |
|
132 gst_gio_base_src_stop (GstBaseSrc * base_src) |
|
133 { |
|
134 GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); |
|
135 gboolean success; |
|
136 GError *err = NULL; |
|
137 |
|
138 if (G_IS_INPUT_STREAM (src->stream)) { |
|
139 GST_DEBUG_OBJECT (src, "closing stream"); |
|
140 |
|
141 /* FIXME: can block but unfortunately we can't use async operations |
|
142 * here because they require a running main loop */ |
|
143 success = g_input_stream_close (src->stream, src->cancel, &err); |
|
144 |
|
145 if (!success && !gst_gio_error (src, "g_input_stream_close", &err, NULL)) { |
|
146 GST_ELEMENT_WARNING (src, RESOURCE, CLOSE, (NULL), |
|
147 ("g_input_stream_close failed: %s", err->message)); |
|
148 g_clear_error (&err); |
|
149 } else if (!success) { |
|
150 GST_ELEMENT_WARNING (src, RESOURCE, CLOSE, (NULL), |
|
151 ("g_input_stream_close failed")); |
|
152 } else { |
|
153 GST_DEBUG_OBJECT (src, "g_input_stream_close succeeded"); |
|
154 } |
|
155 |
|
156 g_object_unref (src->stream); |
|
157 src->stream = NULL; |
|
158 } |
|
159 |
|
160 return TRUE; |
|
161 } |
|
162 |
|
163 static gboolean |
|
164 gst_gio_base_src_get_size (GstBaseSrc * base_src, guint64 * size) |
|
165 { |
|
166 GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); |
|
167 |
|
168 if (G_IS_FILE_INPUT_STREAM (src->stream)) { |
|
169 GFileInfo *info; |
|
170 GError *err = NULL; |
|
171 |
|
172 info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (src->stream), |
|
173 G_FILE_ATTRIBUTE_STANDARD_SIZE, src->cancel, &err); |
|
174 |
|
175 if (info != NULL) { |
|
176 *size = g_file_info_get_size (info); |
|
177 g_object_unref (info); |
|
178 GST_DEBUG_OBJECT (src, "found size: %" G_GUINT64_FORMAT, *size); |
|
179 return TRUE; |
|
180 } |
|
181 |
|
182 if (!gst_gio_error (src, "g_file_input_stream_query_info", &err, NULL)) { |
|
183 |
|
184 if (GST_GIO_ERROR_MATCHES (err, NOT_SUPPORTED)) |
|
185 GST_DEBUG_OBJECT (src, "size information not available"); |
|
186 else |
|
187 GST_WARNING_OBJECT (src, "size information retrieval failed: %s", |
|
188 err->message); |
|
189 |
|
190 g_clear_error (&err); |
|
191 } |
|
192 } else if (GST_GIO_STREAM_IS_SEEKABLE (src->stream)) { |
|
193 goffset old; |
|
194 goffset stream_size; |
|
195 gboolean ret; |
|
196 GSeekable *seekable = G_SEEKABLE (src->stream); |
|
197 GError *err = NULL; |
|
198 |
|
199 old = g_seekable_tell (seekable); |
|
200 |
|
201 ret = g_seekable_seek (seekable, 0, G_SEEK_END, src->cancel, &err); |
|
202 if (!ret) { |
|
203 if (!gst_gio_error (src, "g_seekable_seek", &err, NULL)) { |
|
204 if (GST_GIO_ERROR_MATCHES (err, NOT_SUPPORTED)) |
|
205 GST_DEBUG_OBJECT (src, |
|
206 "Seeking to the end of stream is not supported"); |
|
207 else |
|
208 GST_WARNING_OBJECT (src, "Seeking to end of stream failed: %s", |
|
209 err->message); |
|
210 g_clear_error (&err); |
|
211 } else { |
|
212 GST_WARNING_OBJECT (src, "Seeking to end of stream failed"); |
|
213 } |
|
214 |
|
215 return FALSE; |
|
216 } |
|
217 |
|
218 stream_size = g_seekable_tell (seekable); |
|
219 |
|
220 ret = g_seekable_seek (seekable, old, G_SEEK_SET, src->cancel, &err); |
|
221 if (!ret) { |
|
222 if (!gst_gio_error (src, "g_seekable_seek", &err, NULL)) { |
|
223 if (GST_GIO_ERROR_MATCHES (err, NOT_SUPPORTED)) |
|
224 GST_ERROR_OBJECT (src, "Seeking to the old position not supported"); |
|
225 else |
|
226 GST_ERROR_OBJECT (src, "Seeking to the old position failed: %s", |
|
227 err->message); |
|
228 g_clear_error (&err); |
|
229 } else { |
|
230 GST_ERROR_OBJECT (src, "Seeking to the old position faile"); |
|
231 } |
|
232 |
|
233 return FALSE; |
|
234 } |
|
235 |
|
236 if (stream_size >= 0) { |
|
237 *size = stream_size; |
|
238 return TRUE; |
|
239 } |
|
240 } |
|
241 |
|
242 return FALSE; |
|
243 } |
|
244 |
|
245 static gboolean |
|
246 gst_gio_base_src_is_seekable (GstBaseSrc * base_src) |
|
247 { |
|
248 GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); |
|
249 gboolean seekable; |
|
250 |
|
251 seekable = GST_GIO_STREAM_IS_SEEKABLE (src->stream); |
|
252 |
|
253 GST_DEBUG_OBJECT (src, "can seek: %d", seekable); |
|
254 |
|
255 return seekable; |
|
256 } |
|
257 |
|
258 static gboolean |
|
259 gst_gio_base_src_unlock (GstBaseSrc * base_src) |
|
260 { |
|
261 GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); |
|
262 |
|
263 GST_LOG_OBJECT (src, "triggering cancellation"); |
|
264 |
|
265 g_cancellable_cancel (src->cancel); |
|
266 |
|
267 return TRUE; |
|
268 } |
|
269 |
|
270 static gboolean |
|
271 gst_gio_base_src_unlock_stop (GstBaseSrc * base_src) |
|
272 { |
|
273 GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); |
|
274 |
|
275 GST_LOG_OBJECT (src, "resetting cancellable"); |
|
276 |
|
277 g_cancellable_reset (src->cancel); |
|
278 |
|
279 return TRUE; |
|
280 } |
|
281 |
|
282 static gboolean |
|
283 gst_gio_base_src_check_get_range (GstBaseSrc * base_src) |
|
284 { |
|
285 /* FIXME: Implement dry-run variant using guesswork like gnomevfssrc? */ |
|
286 |
|
287 return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS, |
|
288 check_get_range, (base_src), FALSE); |
|
289 } |
|
290 |
|
291 static GstFlowReturn |
|
292 gst_gio_base_src_create (GstBaseSrc * base_src, guint64 offset, guint size, |
|
293 GstBuffer ** buf_return) |
|
294 { |
|
295 GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); |
|
296 GstBuffer *buf; |
|
297 gssize read; |
|
298 gboolean success, eos; |
|
299 GstFlowReturn ret = GST_FLOW_OK; |
|
300 GError *err = NULL; |
|
301 |
|
302 g_return_val_if_fail (G_IS_INPUT_STREAM (src->stream), GST_FLOW_ERROR); |
|
303 |
|
304 if (G_UNLIKELY (offset != src->position)) { |
|
305 if (!GST_GIO_STREAM_IS_SEEKABLE (src->stream)) |
|
306 return GST_FLOW_NOT_SUPPORTED; |
|
307 |
|
308 ret = gst_gio_seek (src, G_SEEKABLE (src->stream), offset, src->cancel); |
|
309 |
|
310 if (ret == GST_FLOW_OK) |
|
311 src->position = offset; |
|
312 else |
|
313 return ret; |
|
314 } |
|
315 |
|
316 buf = gst_buffer_new_and_alloc (size); |
|
317 |
|
318 GST_LOG_OBJECT (src, "reading %u bytes from offset %" G_GUINT64_FORMAT, |
|
319 size, offset); |
|
320 |
|
321 read = |
|
322 g_input_stream_read (G_INPUT_STREAM (src->stream), GST_BUFFER_DATA (buf), |
|
323 size, src->cancel, &err); |
|
324 |
|
325 success = (read >= 0); |
|
326 eos = (size > 0 && read == 0); |
|
327 |
|
328 if (!success && !gst_gio_error (src, "g_input_stream_read", &err, &ret)) { |
|
329 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), |
|
330 ("Could not read from stream: %s", err->message)); |
|
331 g_clear_error (&err); |
|
332 } |
|
333 |
|
334 if (success && !eos) { |
|
335 src->position += read; |
|
336 GST_BUFFER_OFFSET (buf) = offset; |
|
337 GST_BUFFER_SIZE (buf) = read; |
|
338 *buf_return = buf; |
|
339 } else { |
|
340 /* !success || eos */ |
|
341 gst_buffer_unref (buf); |
|
342 } |
|
343 |
|
344 if (eos) |
|
345 ret = GST_FLOW_UNEXPECTED; |
|
346 |
|
347 return ret; |
|
348 } |
|
349 |
|
350 void |
|
351 gst_gio_base_src_set_stream (GstGioBaseSrc * src, GInputStream * stream) |
|
352 { |
|
353 gboolean success; |
|
354 GError *err = NULL; |
|
355 |
|
356 g_return_if_fail (G_IS_INPUT_STREAM (stream)); |
|
357 g_return_if_fail ((GST_STATE (src) != GST_STATE_PLAYING && |
|
358 GST_STATE (src) != GST_STATE_PAUSED)); |
|
359 |
|
360 if (G_IS_INPUT_STREAM (src->stream)) { |
|
361 GST_DEBUG_OBJECT (src, "closing old stream"); |
|
362 |
|
363 /* FIXME: can block but unfortunately we can't use async operations |
|
364 * here because they require a running main loop */ |
|
365 success = g_input_stream_close (src->stream, src->cancel, &err); |
|
366 |
|
367 if (!success && !gst_gio_error (src, "g_input_stream_close", &err, NULL)) { |
|
368 GST_ELEMENT_WARNING (src, RESOURCE, CLOSE, (NULL), |
|
369 ("g_input_stream_close failed: %s", err->message)); |
|
370 g_clear_error (&err); |
|
371 } else if (!success) { |
|
372 GST_ELEMENT_WARNING (src, RESOURCE, CLOSE, (NULL), |
|
373 ("g_input_stream_close failed")); |
|
374 } else { |
|
375 GST_DEBUG_OBJECT (src, "g_input_stream_close succeeded"); |
|
376 } |
|
377 |
|
378 g_object_unref (src->stream); |
|
379 src->stream = NULL; |
|
380 } |
|
381 |
|
382 src->stream = stream; |
|
383 } |
|