|
1 /* GStreamer |
|
2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> |
|
3 * |
|
4 * This library is free software; you can redistribute it and/or |
|
5 * modify it under the terms of the GNU Library General Public |
|
6 * License as published by the Free Software Foundation; either |
|
7 * version 2 of the License, or (at your option) any later version. |
|
8 * |
|
9 * This library is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 * Library General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU Library General Public |
|
15 * License along with this library; if not, write to the |
|
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|
17 * Boston, MA 02111-1307, USA. |
|
18 */ |
|
19 |
|
20 /** |
|
21 * SECTION:element-uridecodebin |
|
22 * @short_description: decoder for an uri |
|
23 * |
|
24 * Decodes data from a URI into raw media. |
|
25 */ |
|
26 |
|
27 #ifdef HAVE_CONFIG_H |
|
28 # include "config.h" |
|
29 #endif |
|
30 |
|
31 #include <string.h> |
|
32 |
|
33 #include <gst/gst.h> |
|
34 #include <gst/gst-i18n-plugin.h> |
|
35 |
|
36 #include "gstplay-marshal.h" |
|
37 #include "gstplay-enum.h" |
|
38 |
|
39 #ifdef __SYMBIAN32__ |
|
40 #include <glib_global.h> |
|
41 #endif |
|
42 #define GST_TYPE_URI_DECODE_BIN \ |
|
43 (gst_uri_decode_bin_get_type()) |
|
44 #define GST_URI_DECODE_BIN(obj) \ |
|
45 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN,GstURIDecodeBin)) |
|
46 #define GST_URI_DECODE_BIN_CLASS(klass) \ |
|
47 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN,GstURIDecodeBinClass)) |
|
48 #define GST_IS_URI_DECODE_BIN(obj) \ |
|
49 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN)) |
|
50 #define GST_IS_URI_DECODE_BIN_CLASS(klass) \ |
|
51 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN)) |
|
52 |
|
53 typedef struct _GstURIDecodeBin GstURIDecodeBin; |
|
54 typedef struct _GstURIDecodeBinClass GstURIDecodeBinClass; |
|
55 |
|
56 struct _GstURIDecodeBin |
|
57 { |
|
58 GstBin parent_instance; |
|
59 |
|
60 gchar *uri; |
|
61 guint connection_speed; |
|
62 GstCaps *caps; |
|
63 gchar *encoding; |
|
64 |
|
65 gboolean is_stream; |
|
66 GstElement *source; |
|
67 GstElement *queue; |
|
68 GSList *decoders; |
|
69 GSList *srcpads; |
|
70 gint numpads; |
|
71 |
|
72 /* for dynamic sources */ |
|
73 guint src_np_sig_id; /* new-pad signal id */ |
|
74 guint src_nmp_sig_id; /* no-more-pads signal id */ |
|
75 gint pending; |
|
76 }; |
|
77 |
|
78 struct _GstURIDecodeBinClass |
|
79 { |
|
80 GstBinClass parent_class; |
|
81 |
|
82 /* signal fired when we found a pad that we cannot decode */ |
|
83 void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps); |
|
84 |
|
85 /* signal fired to know if we continue trying to decode the given caps */ |
|
86 gboolean (*autoplug_continue) (GstElement * element, GstPad * pad, |
|
87 GstCaps * caps); |
|
88 /* signal fired to get a list of factories to try to autoplug */ |
|
89 GValueArray *(*autoplug_factories) (GstElement * element, GstPad * pad, |
|
90 GstCaps * caps); |
|
91 /* signal fired to select from the proposed list of factories */ |
|
92 GstAutoplugSelectResult (*autoplug_select) (GstElement * element, |
|
93 GstPad * pad, GstCaps * caps, GValueArray * factories); |
|
94 |
|
95 /* emited when all data is decoded */ |
|
96 void (*drained) (GstElement * element); |
|
97 }; |
|
98 |
|
99 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src%d", |
|
100 GST_PAD_SRC, |
|
101 GST_PAD_SOMETIMES, |
|
102 GST_STATIC_CAPS_ANY); |
|
103 |
|
104 GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin_debug); |
|
105 #define GST_CAT_DEFAULT gst_uri_decode_bin_debug |
|
106 |
|
107 static const GstElementDetails gst_uri_decode_bin_details = |
|
108 GST_ELEMENT_DETAILS ("URI Decoder", |
|
109 "Generic/Bin/Decoder", |
|
110 "Autoplug and decode an URI to raw media", |
|
111 "Wim Taymans <wim.taymans@gmail.com>"); |
|
112 |
|
113 /* signals */ |
|
114 enum |
|
115 { |
|
116 SIGNAL_UNKNOWN_TYPE, |
|
117 SIGNAL_AUTOPLUG_CONTINUE, |
|
118 SIGNAL_AUTOPLUG_FACTORIES, |
|
119 SIGNAL_AUTOPLUG_SELECT, |
|
120 SIGNAL_DRAINED, |
|
121 LAST_SIGNAL |
|
122 }; |
|
123 |
|
124 /* properties */ |
|
125 #define DEFAULT_PROP_URI NULL |
|
126 #define DEFAULT_CONNECTION_SPEED 0 |
|
127 #define DEFAULT_CAPS NULL |
|
128 #define DEFAULT_SUBTITLE_ENCODING NULL |
|
129 |
|
130 enum |
|
131 { |
|
132 PROP_0, |
|
133 PROP_URI, |
|
134 PROP_CONNECTION_SPEED, |
|
135 PROP_CAPS, |
|
136 PROP_SUBTITLE_ENCODING, |
|
137 PROP_LAST |
|
138 }; |
|
139 |
|
140 static guint gst_uri_decode_bin_signals[LAST_SIGNAL] = { 0 }; |
|
141 |
|
142 GST_BOILERPLATE (GstURIDecodeBin, gst_uri_decode_bin, GstBin, GST_TYPE_BIN); |
|
143 |
|
144 static void gst_uri_decode_bin_set_property (GObject * object, guint prop_id, |
|
145 const GValue * value, GParamSpec * pspec); |
|
146 static void gst_uri_decode_bin_get_property (GObject * object, guint prop_id, |
|
147 GValue * value, GParamSpec * pspec); |
|
148 static void gst_uri_decode_bin_finalize (GObject * obj); |
|
149 |
|
150 static void handle_message (GstBin * bin, GstMessage * msg); |
|
151 |
|
152 static gboolean gst_uri_decode_bin_query (GstElement * element, |
|
153 GstQuery * query); |
|
154 static GstStateChangeReturn gst_uri_decode_bin_change_state (GstElement * |
|
155 element, GstStateChange transition); |
|
156 |
|
157 static void |
|
158 gst_uri_decode_bin_base_init (gpointer g_class) |
|
159 { |
|
160 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); |
|
161 |
|
162 gst_element_class_add_pad_template (gstelement_class, |
|
163 gst_static_pad_template_get (&srctemplate)); |
|
164 gst_element_class_set_details (gstelement_class, &gst_uri_decode_bin_details); |
|
165 } |
|
166 |
|
167 static gboolean |
|
168 _gst_boolean_accumulator (GSignalInvocationHint * ihint, |
|
169 GValue * return_accu, const GValue * handler_return, gpointer dummy) |
|
170 { |
|
171 gboolean myboolean; |
|
172 |
|
173 myboolean = g_value_get_boolean (handler_return); |
|
174 if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) |
|
175 g_value_set_boolean (return_accu, myboolean); |
|
176 |
|
177 /* stop emission if FALSE */ |
|
178 return myboolean; |
|
179 } |
|
180 |
|
181 static gboolean |
|
182 _gst_array_accumulator (GSignalInvocationHint * ihint, |
|
183 GValue * return_accu, const GValue * handler_return, gpointer dummy) |
|
184 { |
|
185 gpointer array; |
|
186 |
|
187 array = g_value_get_boxed (handler_return); |
|
188 if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) |
|
189 g_value_set_boxed (return_accu, array); |
|
190 |
|
191 return FALSE; |
|
192 } |
|
193 |
|
194 static gboolean |
|
195 _gst_select_accumulator (GSignalInvocationHint * ihint, |
|
196 GValue * return_accu, const GValue * handler_return, gpointer dummy) |
|
197 { |
|
198 GstAutoplugSelectResult res; |
|
199 |
|
200 res = g_value_get_enum (handler_return); |
|
201 if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) |
|
202 g_value_set_enum (return_accu, res); |
|
203 |
|
204 return FALSE; |
|
205 } |
|
206 |
|
207 static gboolean |
|
208 gst_uri_decode_bin_autoplug_continue (GstElement * element, GstPad * pad, |
|
209 GstCaps * caps) |
|
210 { |
|
211 /* by default we always continue */ |
|
212 return TRUE; |
|
213 } |
|
214 |
|
215 static void |
|
216 gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) |
|
217 { |
|
218 GObjectClass *gobject_class; |
|
219 GstElementClass *gstelement_class; |
|
220 GstBinClass *gstbin_class; |
|
221 |
|
222 gobject_class = G_OBJECT_CLASS (klass); |
|
223 gstelement_class = GST_ELEMENT_CLASS (klass); |
|
224 gstbin_class = GST_BIN_CLASS (klass); |
|
225 |
|
226 gobject_class->set_property = gst_uri_decode_bin_set_property; |
|
227 gobject_class->get_property = gst_uri_decode_bin_get_property; |
|
228 gobject_class->finalize = gst_uri_decode_bin_finalize; |
|
229 |
|
230 g_object_class_install_property (gobject_class, PROP_URI, |
|
231 g_param_spec_string ("uri", "URI", "URI to decode", |
|
232 DEFAULT_PROP_URI, G_PARAM_READWRITE)); |
|
233 |
|
234 g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED, |
|
235 g_param_spec_uint ("connection-speed", "Connection Speed", |
|
236 "Network connection speed in kbps (0 = unknown)", |
|
237 0, G_MAXUINT, DEFAULT_CONNECTION_SPEED, G_PARAM_READWRITE)); |
|
238 |
|
239 g_object_class_install_property (gobject_class, PROP_CAPS, |
|
240 g_param_spec_boxed ("caps", "Caps", |
|
241 "The caps on which to stop decoding. (NULL = default)", |
|
242 GST_TYPE_CAPS, G_PARAM_READWRITE)); |
|
243 |
|
244 g_object_class_install_property (gobject_class, PROP_SUBTITLE_ENCODING, |
|
245 g_param_spec_string ("subtitle-encoding", "subtitle encoding", |
|
246 "Encoding to assume if input subtitles are not in UTF-8 encoding. " |
|
247 "If not set, the GST_SUBTITLE_ENCODING environment variable will " |
|
248 "be checked for an encoding to use. If that is not set either, " |
|
249 "ISO-8859-15 will be assumed.", NULL, G_PARAM_READWRITE)); |
|
250 |
|
251 /** |
|
252 * GstURIDecodeBin::unknown-type: |
|
253 * @pad: the new pad containing caps that cannot be resolved to a 'final' stream type. |
|
254 * @caps: the #GstCaps of the pad that cannot be resolved. |
|
255 * |
|
256 * This signal is emitted when a pad for which there is no further possible |
|
257 * decoding is added to the uridecodebin. |
|
258 */ |
|
259 gst_uri_decode_bin_signals[SIGNAL_UNKNOWN_TYPE] = |
|
260 g_signal_new ("unknown-type", G_TYPE_FROM_CLASS (klass), |
|
261 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBinClass, unknown_type), |
|
262 NULL, NULL, gst_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, |
|
263 GST_TYPE_PAD, GST_TYPE_CAPS); |
|
264 |
|
265 /** |
|
266 * GstURIDecodeBin::autoplug-continue: |
|
267 * @pad: The #GstPad. |
|
268 * @caps: The #GstCaps found. |
|
269 * |
|
270 * This signal is emitted whenever uridecodebin finds a new stream. It is |
|
271 * emitted before looking for any elements that can handle that stream. |
|
272 * |
|
273 * Returns: #TRUE if you wish uridecodebin to look for elements that can |
|
274 * handle the given @caps. If #FALSE, those caps will be considered as |
|
275 * final and the pad will be exposed as such (see 'new-decoded-pad' |
|
276 * signal). |
|
277 */ |
|
278 gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE] = |
|
279 g_signal_new ("autoplug-continue", G_TYPE_FROM_CLASS (klass), |
|
280 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBinClass, |
|
281 autoplug_continue), _gst_boolean_accumulator, NULL, |
|
282 gst_play_marshal_BOOLEAN__OBJECT_OBJECT, G_TYPE_BOOLEAN, 2, GST_TYPE_PAD, |
|
283 GST_TYPE_CAPS); |
|
284 |
|
285 /** |
|
286 * GstURIDecodeBin::autoplug-factories: |
|
287 * @pad: The #GstPad. |
|
288 * @caps: The #GstCaps found. |
|
289 * |
|
290 * This function is emited when an array of possible factories for @caps on |
|
291 * @pad is needed. Decodebin2 will by default return |
|
292 * |
|
293 * Returns: a #GValueArray* with a list of factories to try. The factories are |
|
294 * by default tried in the returned order or based on the index returned by |
|
295 * "autoplug-select". |
|
296 */ |
|
297 gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_FACTORIES] = |
|
298 g_signal_new ("autoplug-factories", G_TYPE_FROM_CLASS (klass), |
|
299 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBinClass, |
|
300 autoplug_factories), _gst_array_accumulator, NULL, |
|
301 gst_play_marshal_BOXED__OBJECT_OBJECT, G_TYPE_VALUE_ARRAY, 2, |
|
302 GST_TYPE_PAD, GST_TYPE_CAPS); |
|
303 |
|
304 /** |
|
305 * GstURIDecodeBin::autoplug-select: |
|
306 * @pad: The #GstPad. |
|
307 * @caps: The #GstCaps. |
|
308 * @factories: A #GValueArray of possible #GstElementFactory to use, sorted by |
|
309 * rank (higher ranks come first). |
|
310 * |
|
311 * This signal is emitted once uridecodebin has found all the possible |
|
312 * #GstElementFactory that can be used to handle the given @caps. |
|
313 * |
|
314 * Returns: A #gint indicating what factory index from the @factories array |
|
315 * that you wish uridecodebin to use for trying to decode the given @caps. |
|
316 * -1 to stop selection of a factory. The default handler always |
|
317 * returns the first possible factory. |
|
318 */ |
|
319 gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_SELECT] = |
|
320 g_signal_new ("autoplug-select", G_TYPE_FROM_CLASS (klass), |
|
321 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBinClass, |
|
322 autoplug_select), _gst_select_accumulator, NULL, |
|
323 gst_play_marshal_ENUM__OBJECT_OBJECT_OBJECT, |
|
324 GST_TYPE_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS, |
|
325 GST_TYPE_ELEMENT_FACTORY); |
|
326 |
|
327 /** |
|
328 * GstURIDecodeBin::drained: |
|
329 * |
|
330 * This signal is emitted when the data for the current uri is played. |
|
331 */ |
|
332 gst_uri_decode_bin_signals[SIGNAL_DRAINED] = |
|
333 g_signal_new ("drained", G_TYPE_FROM_CLASS (klass), |
|
334 G_SIGNAL_RUN_LAST, |
|
335 G_STRUCT_OFFSET (GstURIDecodeBinClass, drained), NULL, NULL, |
|
336 gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); |
|
337 |
|
338 gstelement_class->query = GST_DEBUG_FUNCPTR (gst_uri_decode_bin_query); |
|
339 gstelement_class->change_state = |
|
340 GST_DEBUG_FUNCPTR (gst_uri_decode_bin_change_state); |
|
341 |
|
342 gstbin_class->handle_message = GST_DEBUG_FUNCPTR (handle_message); |
|
343 |
|
344 klass->autoplug_continue = |
|
345 GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_continue); |
|
346 } |
|
347 |
|
348 static void |
|
349 gst_uri_decode_bin_init (GstURIDecodeBin * dec, GstURIDecodeBinClass * klass) |
|
350 { |
|
351 dec->uri = g_strdup (DEFAULT_PROP_URI); |
|
352 dec->connection_speed = DEFAULT_CONNECTION_SPEED; |
|
353 dec->caps = DEFAULT_CAPS; |
|
354 dec->encoding = g_strdup (DEFAULT_SUBTITLE_ENCODING); |
|
355 } |
|
356 |
|
357 static void |
|
358 gst_uri_decode_bin_finalize (GObject * obj) |
|
359 { |
|
360 GstURIDecodeBin *dec = GST_URI_DECODE_BIN (obj); |
|
361 |
|
362 g_free (dec->uri); |
|
363 g_free (dec->encoding); |
|
364 |
|
365 G_OBJECT_CLASS (parent_class)->finalize (obj); |
|
366 } |
|
367 |
|
368 static void |
|
369 gst_uri_decode_bin_set_property (GObject * object, guint prop_id, |
|
370 const GValue * value, GParamSpec * pspec) |
|
371 { |
|
372 GstURIDecodeBin *dec = GST_URI_DECODE_BIN (object); |
|
373 |
|
374 switch (prop_id) { |
|
375 case PROP_URI: |
|
376 GST_OBJECT_LOCK (dec); |
|
377 g_free (dec->uri); |
|
378 dec->uri = g_value_dup_string (value); |
|
379 GST_OBJECT_UNLOCK (dec); |
|
380 break; |
|
381 case PROP_CONNECTION_SPEED: |
|
382 GST_OBJECT_LOCK (dec); |
|
383 dec->connection_speed = g_value_get_uint (value) * 1000; |
|
384 GST_OBJECT_UNLOCK (dec); |
|
385 break; |
|
386 case PROP_CAPS: |
|
387 GST_OBJECT_LOCK (dec); |
|
388 if (dec->caps) |
|
389 gst_caps_unref (dec->caps); |
|
390 dec->caps = g_value_dup_boxed (value); |
|
391 GST_OBJECT_UNLOCK (dec); |
|
392 break; |
|
393 case PROP_SUBTITLE_ENCODING: |
|
394 GST_OBJECT_LOCK (dec); |
|
395 g_free (dec->encoding); |
|
396 dec->encoding = g_value_dup_string (value); |
|
397 GST_OBJECT_UNLOCK (dec); |
|
398 break; |
|
399 default: |
|
400 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
401 break; |
|
402 } |
|
403 } |
|
404 |
|
405 static void |
|
406 gst_uri_decode_bin_get_property (GObject * object, guint prop_id, |
|
407 GValue * value, GParamSpec * pspec) |
|
408 { |
|
409 GstURIDecodeBin *dec = GST_URI_DECODE_BIN (object); |
|
410 |
|
411 switch (prop_id) { |
|
412 case PROP_URI: |
|
413 GST_OBJECT_LOCK (dec); |
|
414 g_value_set_string (value, dec->uri); |
|
415 GST_OBJECT_UNLOCK (dec); |
|
416 break; |
|
417 case PROP_CONNECTION_SPEED: |
|
418 GST_OBJECT_LOCK (dec); |
|
419 g_value_set_uint (value, dec->connection_speed / 1000); |
|
420 GST_OBJECT_UNLOCK (dec); |
|
421 break; |
|
422 case PROP_CAPS: |
|
423 GST_OBJECT_LOCK (dec); |
|
424 g_value_set_boxed (value, dec->caps); |
|
425 GST_OBJECT_UNLOCK (dec); |
|
426 break; |
|
427 case PROP_SUBTITLE_ENCODING: |
|
428 GST_OBJECT_LOCK (dec); |
|
429 g_value_set_string (value, dec->encoding); |
|
430 GST_OBJECT_UNLOCK (dec); |
|
431 break; |
|
432 default: |
|
433 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
434 break; |
|
435 } |
|
436 } |
|
437 |
|
438 #define DEFAULT_QUEUE_SIZE (3 * GST_SECOND) |
|
439 #define DEFAULT_QUEUE_MIN_THRESHOLD ((DEFAULT_QUEUE_SIZE * 30) / 100) |
|
440 #define DEFAULT_QUEUE_THRESHOLD ((DEFAULT_QUEUE_SIZE * 95) / 100) |
|
441 |
|
442 static void |
|
443 unknown_type_cb (GstElement * element, GstPad * pad, GstCaps * caps, |
|
444 GstURIDecodeBin * decoder) |
|
445 { |
|
446 gchar *capsstr; |
|
447 |
|
448 capsstr = gst_caps_to_string (caps); |
|
449 GST_ELEMENT_WARNING (decoder, STREAM, WRONG_TYPE, |
|
450 (_("No decoder available for type \'%s\'."), capsstr), (NULL)); |
|
451 g_free (capsstr); |
|
452 } |
|
453 |
|
454 /* add a streaminfo that indicates that the stream is handled by the |
|
455 * given element. This usually means that a stream without actual data is |
|
456 * produced but one that is sunken by an element. Examples of this are: |
|
457 * cdaudio, a hardware decoder/sink, dvd meta bins etc... |
|
458 */ |
|
459 static void |
|
460 add_element_stream (GstElement * element, GstURIDecodeBin * decoder) |
|
461 { |
|
462 g_warning ("add element stream"); |
|
463 } |
|
464 |
|
465 /* when the decoder element signals that no more pads will be generated, we |
|
466 * can commit the current group. |
|
467 */ |
|
468 static void |
|
469 no_more_pads_full (GstElement * element, gboolean subs, |
|
470 GstURIDecodeBin * decoder) |
|
471 { |
|
472 gboolean final; |
|
473 |
|
474 /* setup phase */ |
|
475 GST_DEBUG_OBJECT (element, "no more pads, %d pending", decoder->pending); |
|
476 |
|
477 GST_OBJECT_LOCK (decoder); |
|
478 final = (decoder->pending == 0); |
|
479 |
|
480 /* nothing pending, we can exit */ |
|
481 if (final) |
|
482 goto done; |
|
483 |
|
484 /* the object has no pending no_more_pads */ |
|
485 if (!g_object_get_data (G_OBJECT (element), "pending")) |
|
486 goto done; |
|
487 g_object_set_data (G_OBJECT (element), "pending", NULL); |
|
488 |
|
489 decoder->pending--; |
|
490 final = (decoder->pending == 0); |
|
491 |
|
492 done: |
|
493 GST_OBJECT_UNLOCK (decoder); |
|
494 |
|
495 if (final) |
|
496 gst_element_no_more_pads (GST_ELEMENT_CAST (decoder)); |
|
497 |
|
498 return; |
|
499 } |
|
500 |
|
501 static void |
|
502 no_more_pads (GstElement * element, GstURIDecodeBin * decoder) |
|
503 { |
|
504 no_more_pads_full (element, FALSE, decoder); |
|
505 } |
|
506 |
|
507 static void |
|
508 source_no_more_pads (GstElement * element, GstURIDecodeBin * bin) |
|
509 { |
|
510 GST_DEBUG_OBJECT (bin, "No more pads in source element %s.", |
|
511 GST_ELEMENT_NAME (element)); |
|
512 |
|
513 g_signal_handler_disconnect (G_OBJECT (element), bin->src_np_sig_id); |
|
514 bin->src_np_sig_id = 0; |
|
515 g_signal_handler_disconnect (G_OBJECT (element), bin->src_nmp_sig_id); |
|
516 bin->src_nmp_sig_id = 0; |
|
517 |
|
518 no_more_pads_full (element, FALSE, bin); |
|
519 } |
|
520 |
|
521 /* Called by the signal handlers when a decodebin has |
|
522 * found a new raw pad. |
|
523 */ |
|
524 static void |
|
525 new_decoded_pad_cb (GstElement * element, GstPad * pad, gboolean last, |
|
526 GstURIDecodeBin * decoder) |
|
527 { |
|
528 GstPad *newpad; |
|
529 gchar *padname; |
|
530 |
|
531 GST_DEBUG_OBJECT (element, "new decoded pad, name: <%s>. Last: %d", |
|
532 GST_PAD_NAME (pad), last); |
|
533 |
|
534 GST_OBJECT_LOCK (decoder); |
|
535 padname = g_strdup_printf ("src%d", decoder->numpads); |
|
536 decoder->numpads++; |
|
537 GST_OBJECT_UNLOCK (decoder); |
|
538 |
|
539 newpad = gst_ghost_pad_new (padname, pad); |
|
540 g_free (padname); |
|
541 |
|
542 /* store ref to the ghostpad so we can remove it */ |
|
543 g_object_set_data (G_OBJECT (pad), "uridecodebin.ghostpad", newpad); |
|
544 |
|
545 gst_pad_set_active (newpad, TRUE); |
|
546 gst_element_add_pad (GST_ELEMENT_CAST (decoder), newpad); |
|
547 } |
|
548 |
|
549 static void |
|
550 pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin * decoder) |
|
551 { |
|
552 GstPad *ghost; |
|
553 |
|
554 GST_DEBUG_OBJECT (element, "pad removed name: <%s:%s>", |
|
555 GST_DEBUG_PAD_NAME (pad)); |
|
556 |
|
557 if (!(ghost = g_object_get_data (G_OBJECT (pad), "uridecodebin.ghostpad"))) |
|
558 goto no_ghost; |
|
559 |
|
560 /* unghost the pad */ |
|
561 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ghost), NULL); |
|
562 |
|
563 /* deactivate and remove */ |
|
564 gst_pad_set_active (pad, FALSE); |
|
565 gst_element_remove_pad (GST_ELEMENT_CAST (decoder), ghost); |
|
566 |
|
567 return; |
|
568 |
|
569 /* ERRORS */ |
|
570 no_ghost: |
|
571 { |
|
572 GST_WARNING_OBJECT (element, "no ghost pad found"); |
|
573 return; |
|
574 } |
|
575 } |
|
576 |
|
577 /* helper function to lookup stuff in lists */ |
|
578 static gboolean |
|
579 array_has_value (const gchar * values[], const gchar * value) |
|
580 { |
|
581 gint i; |
|
582 |
|
583 for (i = 0; values[i]; i++) { |
|
584 if (g_str_has_prefix (value, values[i])) |
|
585 return TRUE; |
|
586 } |
|
587 return FALSE; |
|
588 } |
|
589 |
|
590 /* list of URIs that we consider to be streams and that need buffering. |
|
591 * We have no mechanism yet to figure this out with a query. */ |
|
592 static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://", |
|
593 "mmsu://", "mmst://", NULL |
|
594 }; |
|
595 |
|
596 /* blacklisted URIs, we know they will always fail. */ |
|
597 static const gchar *blacklisted_uris[] = { NULL }; |
|
598 |
|
599 /* mime types that we don't consider to be media types */ |
|
600 #if 0 |
|
601 static const gchar *no_media_mimes[] = { |
|
602 "application/x-executable", "application/x-bzip", "application/x-gzip", |
|
603 "application/zip", "application/x-compress", NULL |
|
604 }; |
|
605 #endif |
|
606 |
|
607 /* mime types we consider raw media */ |
|
608 static const gchar *raw_mimes[] = { |
|
609 "audio/x-raw", "video/x-raw", NULL |
|
610 }; |
|
611 |
|
612 #define IS_STREAM_URI(uri) (array_has_value (stream_uris, uri)) |
|
613 #define IS_BLACKLISTED_URI(uri) (array_has_value (blacklisted_uris, uri)) |
|
614 #define IS_NO_MEDIA_MIME(mime) (array_has_value (no_media_mimes, mime)) |
|
615 #define IS_RAW_MIME(mime) (array_has_value (raw_mimes, mime)) |
|
616 |
|
617 /* |
|
618 * Generate and configure a source element. |
|
619 */ |
|
620 static GstElement * |
|
621 gen_source_element (GstURIDecodeBin * decoder) |
|
622 { |
|
623 GstElement *source; |
|
624 |
|
625 if (!decoder->uri) |
|
626 goto no_uri; |
|
627 |
|
628 if (!gst_uri_is_valid (decoder->uri)) |
|
629 goto invalid_uri; |
|
630 |
|
631 if (IS_BLACKLISTED_URI (decoder->uri)) |
|
632 goto uri_blacklisted; |
|
633 |
|
634 source = gst_element_make_from_uri (GST_URI_SRC, decoder->uri, "source"); |
|
635 if (!source) |
|
636 goto no_source; |
|
637 |
|
638 decoder->is_stream = IS_STREAM_URI (decoder->uri); |
|
639 |
|
640 /* make HTTP sources send extra headers so we get icecast |
|
641 * metadata in case the stream is an icecast stream */ |
|
642 if (!strncmp (decoder->uri, "http://", 7) && |
|
643 g_object_class_find_property (G_OBJECT_GET_CLASS (source), |
|
644 "iradio-mode")) { |
|
645 g_object_set (source, "iradio-mode", TRUE, NULL); |
|
646 } |
|
647 |
|
648 if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), |
|
649 "connection-speed")) { |
|
650 GST_DEBUG_OBJECT (decoder, |
|
651 "setting connection-speed=%d to source element", |
|
652 decoder->connection_speed / 1000); |
|
653 g_object_set (source, "connection-speed", |
|
654 decoder->connection_speed / 1000, NULL); |
|
655 } |
|
656 |
|
657 return source; |
|
658 |
|
659 /* ERRORS */ |
|
660 no_uri: |
|
661 { |
|
662 GST_ELEMENT_ERROR (decoder, RESOURCE, NOT_FOUND, |
|
663 (_("No URI specified to play from.")), (NULL)); |
|
664 return NULL; |
|
665 } |
|
666 invalid_uri: |
|
667 { |
|
668 GST_ELEMENT_ERROR (decoder, RESOURCE, NOT_FOUND, |
|
669 (_("Invalid URI \"%s\"."), decoder->uri), (NULL)); |
|
670 return NULL; |
|
671 } |
|
672 uri_blacklisted: |
|
673 { |
|
674 GST_ELEMENT_ERROR (decoder, RESOURCE, FAILED, |
|
675 (_("This stream type cannot be played yet.")), (NULL)); |
|
676 return NULL; |
|
677 } |
|
678 no_source: |
|
679 { |
|
680 gchar *prot = gst_uri_get_protocol (decoder->uri); |
|
681 |
|
682 /* whoops, could not create the source element, dig a little deeper to |
|
683 * figure out what might be wrong. */ |
|
684 if (prot) { |
|
685 GST_ELEMENT_ERROR (decoder, RESOURCE, FAILED, |
|
686 (_("No URI handler implemented for \"%s\"."), prot), (NULL)); |
|
687 g_free (prot); |
|
688 } else |
|
689 goto invalid_uri; |
|
690 |
|
691 return NULL; |
|
692 } |
|
693 } |
|
694 |
|
695 /** |
|
696 * has_all_raw_caps: |
|
697 * @pad: a #GstPad |
|
698 * @all_raw: pointer to hold the result |
|
699 * |
|
700 * check if the caps of the pad are all raw. The caps are all raw if |
|
701 * all of its structures contain audio/x-raw or video/x-raw. |
|
702 * |
|
703 * Returns: %FALSE @pad has no caps. Else TRUE and @all_raw set t the result. |
|
704 */ |
|
705 static gboolean |
|
706 has_all_raw_caps (GstPad * pad, gboolean * all_raw) |
|
707 { |
|
708 GstCaps *caps; |
|
709 gint capssize; |
|
710 guint i, num_raw = 0; |
|
711 gboolean res = FALSE; |
|
712 |
|
713 caps = gst_pad_get_caps (pad); |
|
714 if (caps == NULL) |
|
715 return FALSE; |
|
716 |
|
717 capssize = gst_caps_get_size (caps); |
|
718 /* no caps, skip and move to the next pad */ |
|
719 if (capssize == 0 || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) |
|
720 goto done; |
|
721 |
|
722 /* count the number of raw formats in the caps */ |
|
723 for (i = 0; i < capssize; ++i) { |
|
724 GstStructure *s; |
|
725 const gchar *mime_type; |
|
726 |
|
727 s = gst_caps_get_structure (caps, i); |
|
728 mime_type = gst_structure_get_name (s); |
|
729 |
|
730 if (IS_RAW_MIME (mime_type)) |
|
731 ++num_raw; |
|
732 } |
|
733 |
|
734 *all_raw = (num_raw == capssize); |
|
735 res = TRUE; |
|
736 |
|
737 done: |
|
738 gst_caps_unref (caps); |
|
739 return res; |
|
740 } |
|
741 |
|
742 /** |
|
743 * analyse_source: |
|
744 * @decoder: a #GstURIDecodeBin |
|
745 * @is_raw: are all pads raw data |
|
746 * @have_out: does the source have output |
|
747 * @is_dynamic: is this a dynamic source |
|
748 * |
|
749 * Check the source of @decoder and collect information about it. |
|
750 * |
|
751 * @is_raw will be set to TRUE if the source only produces raw pads. When this |
|
752 * function returns, all of the raw pad of the source will be added |
|
753 * to @decoder. |
|
754 * |
|
755 * @have_out: will be set to TRUE if the source has output pads. |
|
756 * |
|
757 * @is_dynamic: TRUE if the element will create (more) pads dynamically later |
|
758 * on. |
|
759 * |
|
760 * Returns: FALSE if a fatal error occured while scanning. |
|
761 */ |
|
762 static gboolean |
|
763 analyse_source (GstURIDecodeBin * decoder, gboolean * is_raw, |
|
764 gboolean * have_out, gboolean * is_dynamic) |
|
765 { |
|
766 GstIterator *pads_iter; |
|
767 gboolean done = FALSE; |
|
768 gboolean res = TRUE; |
|
769 |
|
770 *have_out = FALSE; |
|
771 *is_raw = FALSE; |
|
772 *is_dynamic = FALSE; |
|
773 |
|
774 pads_iter = gst_element_iterate_src_pads (decoder->source); |
|
775 while (!done) { |
|
776 GstPad *pad; |
|
777 |
|
778 switch (gst_iterator_next (pads_iter, (gpointer) & pad)) { |
|
779 case GST_ITERATOR_ERROR: |
|
780 res = FALSE; |
|
781 /* FALLTROUGH */ |
|
782 case GST_ITERATOR_DONE: |
|
783 done = TRUE; |
|
784 break; |
|
785 case GST_ITERATOR_RESYNC: |
|
786 /* reset results and resync */ |
|
787 *have_out = FALSE; |
|
788 *is_raw = FALSE; |
|
789 *is_dynamic = FALSE; |
|
790 gst_iterator_resync (pads_iter); |
|
791 break; |
|
792 case GST_ITERATOR_OK: |
|
793 /* we now officially have an ouput pad */ |
|
794 *have_out = TRUE; |
|
795 |
|
796 /* if FALSE, this pad has no caps and we continue with the next pad. */ |
|
797 if (!has_all_raw_caps (pad, is_raw)) |
|
798 break; |
|
799 |
|
800 /* caps on source pad are all raw, we can add the pad */ |
|
801 if (*is_raw) |
|
802 new_decoded_pad_cb (decoder->source, pad, FALSE, decoder); |
|
803 break; |
|
804 } |
|
805 } |
|
806 gst_iterator_free (pads_iter); |
|
807 |
|
808 if (!*have_out) { |
|
809 GstElementClass *elemclass; |
|
810 GList *walk; |
|
811 |
|
812 /* element has no output pads, check for padtemplates that list SOMETIMES |
|
813 * pads. */ |
|
814 elemclass = GST_ELEMENT_GET_CLASS (decoder->source); |
|
815 |
|
816 walk = gst_element_class_get_pad_template_list (elemclass); |
|
817 while (walk != NULL) { |
|
818 GstPadTemplate *templ; |
|
819 |
|
820 templ = (GstPadTemplate *) walk->data; |
|
821 if (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) { |
|
822 if (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_SOMETIMES) |
|
823 *is_dynamic = TRUE; |
|
824 break; |
|
825 } |
|
826 walk = g_list_next (walk); |
|
827 } |
|
828 } |
|
829 |
|
830 return res; |
|
831 } |
|
832 |
|
833 static void |
|
834 remove_decoders (GstURIDecodeBin * bin) |
|
835 { |
|
836 GSList *walk; |
|
837 |
|
838 for (walk = bin->decoders; walk; walk = g_slist_next (walk)) { |
|
839 GstElement *decoder = GST_ELEMENT_CAST (walk->data); |
|
840 |
|
841 GST_DEBUG_OBJECT (bin, "removing old decoder element"); |
|
842 gst_element_set_state (decoder, GST_STATE_NULL); |
|
843 gst_bin_remove (GST_BIN_CAST (bin), decoder); |
|
844 } |
|
845 g_slist_free (bin->decoders); |
|
846 bin->decoders = NULL; |
|
847 } |
|
848 |
|
849 static void |
|
850 remove_pads (GstURIDecodeBin * bin) |
|
851 { |
|
852 GSList *walk; |
|
853 |
|
854 for (walk = bin->srcpads; walk; walk = g_slist_next (walk)) { |
|
855 GstPad *pad = GST_PAD_CAST (walk->data); |
|
856 |
|
857 GST_DEBUG_OBJECT (bin, "removing old pad"); |
|
858 gst_pad_set_active (pad, FALSE); |
|
859 gst_element_remove_pad (GST_ELEMENT_CAST (bin), pad); |
|
860 } |
|
861 g_slist_free (bin->srcpads); |
|
862 bin->srcpads = NULL; |
|
863 } |
|
864 |
|
865 static void |
|
866 proxy_unknown_type_signal (GstElement * element, GstPad * pad, GstCaps * caps, |
|
867 GstURIDecodeBin * dec) |
|
868 { |
|
869 GST_DEBUG_OBJECT (dec, "unknown-type signaled"); |
|
870 |
|
871 g_signal_emit (G_OBJECT (dec), |
|
872 gst_uri_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps); |
|
873 } |
|
874 |
|
875 static gboolean |
|
876 proxy_autoplug_continue_signal (GstElement * element, GstPad * pad, |
|
877 GstCaps * caps, GstURIDecodeBin * dec) |
|
878 { |
|
879 gboolean result; |
|
880 |
|
881 g_signal_emit (G_OBJECT (dec), |
|
882 gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE], 0, pad, caps, |
|
883 &result); |
|
884 |
|
885 GST_DEBUG_OBJECT (dec, "autoplug-continue returned %d", result); |
|
886 |
|
887 return result; |
|
888 } |
|
889 |
|
890 static GValueArray * |
|
891 proxy_autoplug_factories_signal (GstElement * element, GstPad * pad, |
|
892 GstCaps * caps, GstURIDecodeBin * dec) |
|
893 { |
|
894 GValueArray *result = NULL; |
|
895 |
|
896 g_signal_emit (G_OBJECT (dec), |
|
897 gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_FACTORIES], 0, pad, caps, |
|
898 &result); |
|
899 |
|
900 GST_DEBUG_OBJECT (dec, "autoplug-factories returned %p", result); |
|
901 |
|
902 return result; |
|
903 } |
|
904 |
|
905 static GstAutoplugSelectResult |
|
906 proxy_autoplug_select_signal (GstElement * element, GstPad * pad, |
|
907 GstCaps * caps, GstElementFactory * factory, GstURIDecodeBin * dec) |
|
908 { |
|
909 GstAutoplugSelectResult result; |
|
910 |
|
911 g_signal_emit (G_OBJECT (dec), |
|
912 gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_SELECT], 0, pad, caps, factory, |
|
913 &result); |
|
914 |
|
915 GST_DEBUG_OBJECT (dec, "autoplug-select returned %d", result); |
|
916 |
|
917 return result; |
|
918 } |
|
919 |
|
920 static void |
|
921 proxy_drained_signal (GstElement * element, GstURIDecodeBin * dec) |
|
922 { |
|
923 GST_DEBUG_OBJECT (dec, "drained signaled"); |
|
924 |
|
925 g_signal_emit (G_OBJECT (dec), |
|
926 gst_uri_decode_bin_signals[SIGNAL_DRAINED], 0, NULL); |
|
927 } |
|
928 |
|
929 static GstElement * |
|
930 make_decoder (GstURIDecodeBin * decoder, gboolean use_queue) |
|
931 { |
|
932 GstElement *result, *decodebin; |
|
933 |
|
934 /* now create the decoder element */ |
|
935 decodebin = gst_element_factory_make ("decodebin2", NULL); |
|
936 if (!decodebin) |
|
937 goto no_decodebin; |
|
938 |
|
939 /* connect signals to proxy */ |
|
940 g_signal_connect (G_OBJECT (decodebin), "unknown-type", |
|
941 G_CALLBACK (proxy_unknown_type_signal), decoder); |
|
942 g_signal_connect (G_OBJECT (decodebin), "autoplug-continue", |
|
943 G_CALLBACK (proxy_autoplug_continue_signal), decoder); |
|
944 g_signal_connect (G_OBJECT (decodebin), "autoplug-factories", |
|
945 G_CALLBACK (proxy_autoplug_factories_signal), decoder); |
|
946 g_signal_connect (G_OBJECT (decodebin), "autoplug-select", |
|
947 G_CALLBACK (proxy_autoplug_select_signal), decoder); |
|
948 g_signal_connect (G_OBJECT (decodebin), "drained", |
|
949 G_CALLBACK (proxy_drained_signal), decoder); |
|
950 |
|
951 if (use_queue) { |
|
952 GstElement *queue; |
|
953 GstPad *gpad, *pad; |
|
954 |
|
955 queue = gst_element_factory_make ("queue2", NULL); |
|
956 if (!queue) |
|
957 goto no_queue2; |
|
958 |
|
959 /* configure the queue as a buffering element */ |
|
960 g_object_set (G_OBJECT (queue), "use-buffering", TRUE, NULL); |
|
961 |
|
962 result = gst_bin_new ("source-bin"); |
|
963 |
|
964 gst_bin_add (GST_BIN_CAST (result), queue); |
|
965 gst_bin_add (GST_BIN_CAST (result), decodebin); |
|
966 |
|
967 gst_element_link (queue, decodebin); |
|
968 |
|
969 pad = gst_element_get_pad (queue, "sink"); |
|
970 gpad = gst_ghost_pad_new (GST_PAD_NAME (pad), pad); |
|
971 gst_object_unref (pad); |
|
972 |
|
973 gst_pad_set_active (gpad, TRUE); |
|
974 gst_element_add_pad (GST_ELEMENT_CAST (result), gpad); |
|
975 } else { |
|
976 result = decodebin; |
|
977 } |
|
978 |
|
979 /* set up callbacks to create the links between decoded data |
|
980 * and video/audio/subtitle rendering/output. */ |
|
981 g_signal_connect (G_OBJECT (decodebin), |
|
982 "new-decoded-pad", G_CALLBACK (new_decoded_pad_cb), decoder); |
|
983 g_signal_connect (G_OBJECT (decodebin), |
|
984 "pad-removed", G_CALLBACK (pad_removed_cb), decoder); |
|
985 g_signal_connect (G_OBJECT (decodebin), "no-more-pads", |
|
986 G_CALLBACK (no_more_pads), decoder); |
|
987 g_signal_connect (G_OBJECT (decodebin), |
|
988 "unknown-type", G_CALLBACK (unknown_type_cb), decoder); |
|
989 g_object_set_data (G_OBJECT (decodebin), "pending", "1"); |
|
990 decoder->pending++; |
|
991 |
|
992 gst_bin_add (GST_BIN_CAST (decoder), result); |
|
993 |
|
994 decoder->decoders = g_slist_prepend (decoder->decoders, result); |
|
995 |
|
996 return result; |
|
997 |
|
998 /* ERRORS */ |
|
999 no_decodebin: |
|
1000 { |
|
1001 GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN, |
|
1002 (_("Could not create \"decodebin2\" element.")), (NULL)); |
|
1003 return NULL; |
|
1004 } |
|
1005 no_queue2: |
|
1006 { |
|
1007 GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN, |
|
1008 (_("Could not create \"queue2\" element.")), (NULL)); |
|
1009 return NULL; |
|
1010 } |
|
1011 } |
|
1012 |
|
1013 static void |
|
1014 remove_source (GstURIDecodeBin * bin) |
|
1015 { |
|
1016 GstElement *source = bin->source; |
|
1017 |
|
1018 if (source) { |
|
1019 GST_DEBUG_OBJECT (bin, "removing old src element"); |
|
1020 gst_element_set_state (source, GST_STATE_NULL); |
|
1021 gst_bin_remove (GST_BIN_CAST (bin), source); |
|
1022 |
|
1023 if (bin->src_np_sig_id) { |
|
1024 g_signal_handler_disconnect (G_OBJECT (source), bin->src_np_sig_id); |
|
1025 bin->src_np_sig_id = 0; |
|
1026 } |
|
1027 if (bin->src_nmp_sig_id) { |
|
1028 g_signal_handler_disconnect (G_OBJECT (source), bin->src_nmp_sig_id); |
|
1029 bin->src_nmp_sig_id = 0; |
|
1030 } |
|
1031 bin->source = NULL; |
|
1032 } |
|
1033 } |
|
1034 |
|
1035 /* is called when a dynamic source element created a new pad. */ |
|
1036 static void |
|
1037 source_new_pad (GstElement * element, GstPad * pad, GstURIDecodeBin * bin) |
|
1038 { |
|
1039 GstElement *decoder; |
|
1040 gboolean is_raw; |
|
1041 |
|
1042 GST_DEBUG_OBJECT (bin, "Found new pad %s.%s in source element %s", |
|
1043 GST_DEBUG_PAD_NAME (pad), GST_ELEMENT_NAME (element)); |
|
1044 |
|
1045 /* if this is a pad with all raw caps, we can expose it */ |
|
1046 if (has_all_raw_caps (pad, &is_raw) && is_raw) { |
|
1047 /* it's all raw, create output pads. */ |
|
1048 new_decoded_pad_cb (element, pad, FALSE, bin); |
|
1049 return; |
|
1050 } |
|
1051 |
|
1052 /* not raw, create decoder */ |
|
1053 decoder = make_decoder (bin, FALSE); |
|
1054 if (!decoder) |
|
1055 goto no_decodebin; |
|
1056 |
|
1057 /* and link to decoder */ |
|
1058 if (!gst_element_link (bin->source, decoder)) |
|
1059 goto could_not_link; |
|
1060 |
|
1061 GST_DEBUG_OBJECT (bin, "linked decoder to new pad"); |
|
1062 |
|
1063 gst_element_set_state (decoder, GST_STATE_PLAYING); |
|
1064 |
|
1065 return; |
|
1066 |
|
1067 /* ERRORS */ |
|
1068 no_decodebin: |
|
1069 { |
|
1070 /* error was posted */ |
|
1071 return; |
|
1072 } |
|
1073 could_not_link: |
|
1074 { |
|
1075 GST_ELEMENT_ERROR (bin, CORE, NEGOTIATION, |
|
1076 (NULL), ("Can't link source to decoder element")); |
|
1077 return; |
|
1078 } |
|
1079 } |
|
1080 |
|
1081 /* construct and run the source and decoder elements until we found |
|
1082 * all the streams or until a preroll queue has been filled. |
|
1083 */ |
|
1084 static gboolean |
|
1085 setup_source (GstURIDecodeBin * decoder) |
|
1086 { |
|
1087 gboolean is_raw, have_out, is_dynamic; |
|
1088 |
|
1089 GST_DEBUG_OBJECT (decoder, "setup source"); |
|
1090 |
|
1091 /* delete old src */ |
|
1092 remove_source (decoder); |
|
1093 |
|
1094 /* create and configure an element that can handle the uri */ |
|
1095 if (!(decoder->source = gen_source_element (decoder))) |
|
1096 goto no_source; |
|
1097 |
|
1098 /* state will be merged later - if file is not found, error will be |
|
1099 * handled by the application right after. */ |
|
1100 gst_bin_add (GST_BIN_CAST (decoder), decoder->source); |
|
1101 |
|
1102 /* remove the old decoders now, if any */ |
|
1103 remove_decoders (decoder); |
|
1104 |
|
1105 /* see if the source element emits raw audio/video all by itself, |
|
1106 * if so, we can create streams for the pads and be done with it. |
|
1107 * Also check that is has source pads, if not, we assume it will |
|
1108 * do everything itself. */ |
|
1109 if (!analyse_source (decoder, &is_raw, &have_out, &is_dynamic)) |
|
1110 goto invalid_source; |
|
1111 |
|
1112 if (is_raw) { |
|
1113 GST_DEBUG_OBJECT (decoder, "Source provides all raw data"); |
|
1114 /* source provides raw data, we added the pads and we can now signal a |
|
1115 * no_more pads because we are done. */ |
|
1116 /* FIXME, actually do this... */ |
|
1117 return TRUE; |
|
1118 } |
|
1119 if (!have_out && !is_dynamic) { |
|
1120 GST_DEBUG_OBJECT (decoder, "Source has no output pads"); |
|
1121 /* create a stream to indicate that this uri is handled by a self |
|
1122 * contained element. We are now done. */ |
|
1123 add_element_stream (decoder->source, decoder); |
|
1124 return TRUE; |
|
1125 } |
|
1126 if (is_dynamic) { |
|
1127 GST_DEBUG_OBJECT (decoder, "Source has dynamic output pads"); |
|
1128 /* connect a handler for the new-pad signal */ |
|
1129 decoder->src_np_sig_id = |
|
1130 g_signal_connect (G_OBJECT (decoder->source), "pad-added", |
|
1131 G_CALLBACK (source_new_pad), decoder); |
|
1132 decoder->src_nmp_sig_id = |
|
1133 g_signal_connect (G_OBJECT (decoder->source), "no-more-pads", |
|
1134 G_CALLBACK (source_no_more_pads), decoder); |
|
1135 g_object_set_data (G_OBJECT (decoder->source), "pending", "1"); |
|
1136 decoder->pending++; |
|
1137 } else { |
|
1138 GstElement *dec_elem; |
|
1139 |
|
1140 GST_DEBUG_OBJECT (decoder, "Pluggin decodebin to source"); |
|
1141 |
|
1142 /* no dynamic source, we can link now */ |
|
1143 dec_elem = make_decoder (decoder, decoder->is_stream); |
|
1144 if (!dec_elem) |
|
1145 goto no_decoder; |
|
1146 |
|
1147 if (!gst_element_link (decoder->source, dec_elem)) |
|
1148 goto could_not_link; |
|
1149 } |
|
1150 return TRUE; |
|
1151 |
|
1152 /* ERRORS */ |
|
1153 no_source: |
|
1154 { |
|
1155 /* error message was already posted */ |
|
1156 return FALSE; |
|
1157 } |
|
1158 invalid_source: |
|
1159 { |
|
1160 GST_ELEMENT_ERROR (decoder, CORE, FAILED, |
|
1161 (_("Source element is invalid.")), (NULL)); |
|
1162 return FALSE; |
|
1163 } |
|
1164 no_decoder: |
|
1165 { |
|
1166 /* message was posted */ |
|
1167 return FALSE; |
|
1168 } |
|
1169 could_not_link: |
|
1170 { |
|
1171 GST_ELEMENT_ERROR (decoder, CORE, NEGOTIATION, |
|
1172 (NULL), ("Can't link source to decoder element")); |
|
1173 return FALSE; |
|
1174 } |
|
1175 } |
|
1176 |
|
1177 static void |
|
1178 value_list_append_structure_list (GValue * list_val, GstStructure ** first, |
|
1179 GList * structure_list) |
|
1180 { |
|
1181 GList *l; |
|
1182 |
|
1183 for (l = structure_list; l != NULL; l = l->next) { |
|
1184 GValue val = { 0, }; |
|
1185 |
|
1186 if (*first == NULL) |
|
1187 *first = gst_structure_copy ((GstStructure *) l->data); |
|
1188 |
|
1189 g_value_init (&val, GST_TYPE_STRUCTURE); |
|
1190 g_value_take_boxed (&val, gst_structure_copy ((GstStructure *) l->data)); |
|
1191 gst_value_list_append_value (list_val, &val); |
|
1192 g_value_unset (&val); |
|
1193 } |
|
1194 } |
|
1195 |
|
1196 /* if it's a redirect message with multiple redirect locations we might |
|
1197 * want to pick a different 'best' location depending on the required |
|
1198 * bitrates and the connection speed */ |
|
1199 static GstMessage * |
|
1200 handle_redirect_message (GstURIDecodeBin * dec, GstMessage * msg) |
|
1201 { |
|
1202 const GValue *locations_list, *location_val; |
|
1203 GstMessage *new_msg; |
|
1204 GstStructure *new_structure = NULL; |
|
1205 GList *l_good = NULL, *l_neutral = NULL, *l_bad = NULL; |
|
1206 GValue new_list = { 0, }; |
|
1207 guint size, i; |
|
1208 |
|
1209 GST_DEBUG_OBJECT (dec, "redirect message: %" GST_PTR_FORMAT, msg); |
|
1210 GST_DEBUG_OBJECT (dec, "connection speed: %u", dec->connection_speed); |
|
1211 |
|
1212 if (dec->connection_speed == 0 || msg->structure == NULL) |
|
1213 return msg; |
|
1214 |
|
1215 locations_list = gst_structure_get_value (msg->structure, "locations"); |
|
1216 if (locations_list == NULL) |
|
1217 return msg; |
|
1218 |
|
1219 size = gst_value_list_get_size (locations_list); |
|
1220 if (size < 2) |
|
1221 return msg; |
|
1222 |
|
1223 /* maintain existing order as much as possible, just sort references |
|
1224 * with too high a bitrate to the end (the assumption being that if |
|
1225 * bitrates are given they are given for all interesting streams and |
|
1226 * that the you-need-at-least-version-xyz redirect has the same bitrate |
|
1227 * as the lowest referenced redirect alternative) */ |
|
1228 for (i = 0; i < size; ++i) { |
|
1229 const GstStructure *s; |
|
1230 gint bitrate = 0; |
|
1231 |
|
1232 location_val = gst_value_list_get_value (locations_list, i); |
|
1233 s = (const GstStructure *) g_value_get_boxed (location_val); |
|
1234 if (!gst_structure_get_int (s, "minimum-bitrate", &bitrate) || bitrate <= 0) { |
|
1235 GST_DEBUG_OBJECT (dec, "no bitrate: %" GST_PTR_FORMAT, s); |
|
1236 l_neutral = g_list_append (l_neutral, (gpointer) s); |
|
1237 } else if (bitrate > dec->connection_speed) { |
|
1238 GST_DEBUG_OBJECT (dec, "bitrate too high: %" GST_PTR_FORMAT, s); |
|
1239 l_bad = g_list_append (l_bad, (gpointer) s); |
|
1240 } else if (bitrate <= dec->connection_speed) { |
|
1241 GST_DEBUG_OBJECT (dec, "bitrate OK: %" GST_PTR_FORMAT, s); |
|
1242 l_good = g_list_append (l_good, (gpointer) s); |
|
1243 } |
|
1244 } |
|
1245 |
|
1246 g_value_init (&new_list, GST_TYPE_LIST); |
|
1247 value_list_append_structure_list (&new_list, &new_structure, l_good); |
|
1248 value_list_append_structure_list (&new_list, &new_structure, l_neutral); |
|
1249 value_list_append_structure_list (&new_list, &new_structure, l_bad); |
|
1250 gst_structure_set_value (new_structure, "locations", &new_list); |
|
1251 g_value_unset (&new_list); |
|
1252 |
|
1253 g_list_free (l_good); |
|
1254 g_list_free (l_neutral); |
|
1255 g_list_free (l_bad); |
|
1256 |
|
1257 new_msg = gst_message_new_element (msg->src, new_structure); |
|
1258 gst_message_unref (msg); |
|
1259 |
|
1260 GST_DEBUG_OBJECT (dec, "new redirect message: %" GST_PTR_FORMAT, new_msg); |
|
1261 return new_msg; |
|
1262 } |
|
1263 |
|
1264 static void |
|
1265 handle_message (GstBin * bin, GstMessage * msg) |
|
1266 { |
|
1267 if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ELEMENT && msg->structure != NULL |
|
1268 && gst_structure_has_name (msg->structure, "redirect")) { |
|
1269 /* sort redirect messages based on the connection speed. This simplifies |
|
1270 * the user of this element as it can in most cases just pick the first item |
|
1271 * of the sorted list as a good redirection candidate. It can of course |
|
1272 * choose something else from the list if it has a better way. */ |
|
1273 msg = handle_redirect_message (GST_URI_DECODE_BIN (bin), msg); |
|
1274 } |
|
1275 GST_BIN_CLASS (parent_class)->handle_message (bin, msg); |
|
1276 } |
|
1277 |
|
1278 /* generic struct passed to all query fold methods |
|
1279 * FIXME, move to core. |
|
1280 */ |
|
1281 typedef struct |
|
1282 { |
|
1283 GstQuery *query; |
|
1284 gint64 min; |
|
1285 gint64 max; |
|
1286 gboolean seekable; |
|
1287 gboolean live; |
|
1288 } QueryFold; |
|
1289 |
|
1290 typedef void (*QueryInitFunction) (GstURIDecodeBin * dec, QueryFold * fold); |
|
1291 typedef void (*QueryDoneFunction) (GstURIDecodeBin * dec, QueryFold * fold); |
|
1292 |
|
1293 /* for duration/position we collect all durations/positions and take |
|
1294 * the MAX of all valid results */ |
|
1295 static void |
|
1296 decoder_query_init (GstURIDecodeBin * dec, QueryFold * fold) |
|
1297 { |
|
1298 fold->min = 0; |
|
1299 fold->max = -1; |
|
1300 fold->seekable = TRUE; |
|
1301 fold->live = 0; |
|
1302 } |
|
1303 |
|
1304 static gboolean |
|
1305 decoder_query_duration_fold (GstPad * item, GValue * ret, QueryFold * fold) |
|
1306 { |
|
1307 if (gst_pad_query (item, fold->query)) { |
|
1308 gint64 duration; |
|
1309 |
|
1310 g_value_set_boolean (ret, TRUE); |
|
1311 |
|
1312 gst_query_parse_duration (fold->query, NULL, &duration); |
|
1313 |
|
1314 GST_DEBUG_OBJECT (item, "got duration %" G_GINT64_FORMAT, duration); |
|
1315 |
|
1316 if (duration > fold->max) |
|
1317 fold->max = duration; |
|
1318 } |
|
1319 gst_object_unref (item); |
|
1320 return TRUE; |
|
1321 } |
|
1322 static void |
|
1323 decoder_query_duration_done (GstURIDecodeBin * dec, QueryFold * fold) |
|
1324 { |
|
1325 GstFormat format; |
|
1326 |
|
1327 gst_query_parse_duration (fold->query, &format, NULL); |
|
1328 /* store max in query result */ |
|
1329 gst_query_set_duration (fold->query, format, fold->max); |
|
1330 |
|
1331 GST_DEBUG ("max duration %" G_GINT64_FORMAT, fold->max); |
|
1332 } |
|
1333 |
|
1334 static gboolean |
|
1335 decoder_query_position_fold (GstPad * item, GValue * ret, QueryFold * fold) |
|
1336 { |
|
1337 if (gst_pad_query (item, fold->query)) { |
|
1338 gint64 position; |
|
1339 |
|
1340 g_value_set_boolean (ret, TRUE); |
|
1341 |
|
1342 gst_query_parse_position (fold->query, NULL, &position); |
|
1343 |
|
1344 GST_DEBUG_OBJECT (item, "got position %" G_GINT64_FORMAT, position); |
|
1345 |
|
1346 if (position > fold->max) |
|
1347 fold->max = position; |
|
1348 } |
|
1349 |
|
1350 gst_object_unref (item); |
|
1351 return TRUE; |
|
1352 } |
|
1353 static void |
|
1354 decoder_query_position_done (GstURIDecodeBin * dec, QueryFold * fold) |
|
1355 { |
|
1356 GstFormat format; |
|
1357 |
|
1358 gst_query_parse_position (fold->query, &format, NULL); |
|
1359 /* store max in query result */ |
|
1360 gst_query_set_position (fold->query, format, fold->max); |
|
1361 |
|
1362 GST_DEBUG_OBJECT (dec, "max position %" G_GINT64_FORMAT, fold->max); |
|
1363 } |
|
1364 |
|
1365 static gboolean |
|
1366 decoder_query_latency_fold (GstPad * item, GValue * ret, QueryFold * fold) |
|
1367 { |
|
1368 if (gst_pad_query (item, fold->query)) { |
|
1369 GstClockTime min, max; |
|
1370 gboolean live; |
|
1371 |
|
1372 g_value_set_boolean (ret, TRUE); |
|
1373 |
|
1374 gst_query_parse_latency (fold->query, &live, &min, &max); |
|
1375 |
|
1376 GST_DEBUG_OBJECT (item, |
|
1377 "got latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT |
|
1378 ", live %d", GST_TIME_ARGS (min), GST_TIME_ARGS (max), live); |
|
1379 |
|
1380 /* for the combined latency we collect the MAX of all min latencies and |
|
1381 * the MIN of all max latencies */ |
|
1382 if (min > fold->min) |
|
1383 fold->min = min; |
|
1384 if (fold->max == -1) |
|
1385 fold->max = max; |
|
1386 else if (max < fold->max) |
|
1387 fold->max = max; |
|
1388 if (fold->live == FALSE) |
|
1389 fold->live = live; |
|
1390 } |
|
1391 |
|
1392 gst_object_unref (item); |
|
1393 return TRUE; |
|
1394 } |
|
1395 static void |
|
1396 decoder_query_latency_done (GstURIDecodeBin * dec, QueryFold * fold) |
|
1397 { |
|
1398 /* store max in query result */ |
|
1399 gst_query_set_latency (fold->query, fold->live, fold->min, fold->max); |
|
1400 |
|
1401 GST_DEBUG_OBJECT (dec, |
|
1402 "latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT |
|
1403 ", live %d", GST_TIME_ARGS (fold->min), GST_TIME_ARGS (fold->max), |
|
1404 fold->live); |
|
1405 } |
|
1406 |
|
1407 /* we are seekable if all srcpads are seekable */ |
|
1408 static gboolean |
|
1409 decoder_query_seeking_fold (GstPad * item, GValue * ret, QueryFold * fold) |
|
1410 { |
|
1411 if (gst_pad_query (item, fold->query)) { |
|
1412 gboolean seekable; |
|
1413 |
|
1414 g_value_set_boolean (ret, TRUE); |
|
1415 gst_query_parse_seeking (fold->query, NULL, &seekable, NULL, NULL); |
|
1416 |
|
1417 GST_DEBUG_OBJECT (item, "got seekable %d", seekable); |
|
1418 |
|
1419 if (fold->seekable == TRUE) |
|
1420 fold->seekable = seekable; |
|
1421 } |
|
1422 gst_object_unref (item); |
|
1423 |
|
1424 return TRUE; |
|
1425 } |
|
1426 static void |
|
1427 decoder_query_seeking_done (GstURIDecodeBin * dec, QueryFold * fold) |
|
1428 { |
|
1429 GstFormat format; |
|
1430 |
|
1431 gst_query_parse_seeking (fold->query, &format, NULL, NULL, NULL); |
|
1432 gst_query_set_seeking (fold->query, format, fold->seekable, 0, -1); |
|
1433 |
|
1434 GST_DEBUG_OBJECT (dec, "seekable %d", fold->seekable); |
|
1435 } |
|
1436 |
|
1437 /* generic fold, return first valid result */ |
|
1438 static gboolean |
|
1439 decoder_query_generic_fold (GstPad * item, GValue * ret, QueryFold * fold) |
|
1440 { |
|
1441 gboolean res; |
|
1442 |
|
1443 if ((res = gst_pad_query (item, fold->query))) { |
|
1444 g_value_set_boolean (ret, TRUE); |
|
1445 GST_DEBUG_OBJECT (item, "answered query %p", fold->query); |
|
1446 } |
|
1447 |
|
1448 gst_object_unref (item); |
|
1449 |
|
1450 /* and stop as soon as we have a valid result */ |
|
1451 return !res; |
|
1452 } |
|
1453 |
|
1454 |
|
1455 /* we're a bin, the default query handler iterates sink elements, which we don't |
|
1456 * have normally. We should just query all source pads. |
|
1457 */ |
|
1458 static gboolean |
|
1459 gst_uri_decode_bin_query (GstElement * element, GstQuery * query) |
|
1460 { |
|
1461 GstURIDecodeBin *decoder; |
|
1462 gboolean res = FALSE; |
|
1463 GstIterator *iter; |
|
1464 GstIteratorFoldFunction fold_func; |
|
1465 QueryInitFunction fold_init = NULL; |
|
1466 QueryDoneFunction fold_done = NULL; |
|
1467 QueryFold fold_data; |
|
1468 GValue ret = { 0 }; |
|
1469 |
|
1470 decoder = GST_URI_DECODE_BIN (element); |
|
1471 |
|
1472 switch (GST_QUERY_TYPE (query)) { |
|
1473 case GST_QUERY_DURATION: |
|
1474 /* iterate and collect durations */ |
|
1475 fold_func = (GstIteratorFoldFunction) decoder_query_duration_fold; |
|
1476 fold_init = decoder_query_init; |
|
1477 fold_done = decoder_query_duration_done; |
|
1478 break; |
|
1479 case GST_QUERY_POSITION: |
|
1480 /* iterate and collect durations */ |
|
1481 fold_func = (GstIteratorFoldFunction) decoder_query_position_fold; |
|
1482 fold_init = decoder_query_init; |
|
1483 fold_done = decoder_query_position_done; |
|
1484 break; |
|
1485 case GST_QUERY_LATENCY: |
|
1486 /* iterate and collect durations */ |
|
1487 fold_func = (GstIteratorFoldFunction) decoder_query_latency_fold; |
|
1488 fold_init = decoder_query_init; |
|
1489 fold_done = decoder_query_latency_done; |
|
1490 break; |
|
1491 case GST_QUERY_SEEKING: |
|
1492 /* iterate and collect durations */ |
|
1493 fold_func = (GstIteratorFoldFunction) decoder_query_seeking_fold; |
|
1494 fold_init = decoder_query_init; |
|
1495 fold_done = decoder_query_seeking_done; |
|
1496 break; |
|
1497 default: |
|
1498 fold_func = (GstIteratorFoldFunction) decoder_query_generic_fold; |
|
1499 break; |
|
1500 } |
|
1501 |
|
1502 fold_data.query = query; |
|
1503 |
|
1504 g_value_init (&ret, G_TYPE_BOOLEAN); |
|
1505 g_value_set_boolean (&ret, FALSE); |
|
1506 |
|
1507 iter = gst_element_iterate_src_pads (element); |
|
1508 GST_DEBUG_OBJECT (element, "Sending query %p (type %d) to src pads", |
|
1509 query, GST_QUERY_TYPE (query)); |
|
1510 |
|
1511 if (fold_init) |
|
1512 fold_init (decoder, &fold_data); |
|
1513 |
|
1514 while (TRUE) { |
|
1515 GstIteratorResult ires; |
|
1516 |
|
1517 ires = gst_iterator_fold (iter, fold_func, &ret, &fold_data); |
|
1518 |
|
1519 switch (ires) { |
|
1520 case GST_ITERATOR_RESYNC: |
|
1521 gst_iterator_resync (iter); |
|
1522 if (fold_init) |
|
1523 fold_init (decoder, &fold_data); |
|
1524 g_value_set_boolean (&ret, FALSE); |
|
1525 break; |
|
1526 case GST_ITERATOR_OK: |
|
1527 case GST_ITERATOR_DONE: |
|
1528 res = g_value_get_boolean (&ret); |
|
1529 if (fold_done != NULL && res) |
|
1530 fold_done (decoder, &fold_data); |
|
1531 goto done; |
|
1532 default: |
|
1533 res = FALSE; |
|
1534 goto done; |
|
1535 } |
|
1536 } |
|
1537 done: |
|
1538 gst_iterator_free (iter); |
|
1539 |
|
1540 return res; |
|
1541 } |
|
1542 |
|
1543 static GstStateChangeReturn |
|
1544 gst_uri_decode_bin_change_state (GstElement * element, |
|
1545 GstStateChange transition) |
|
1546 { |
|
1547 GstStateChangeReturn ret; |
|
1548 GstURIDecodeBin *decoder; |
|
1549 |
|
1550 decoder = GST_URI_DECODE_BIN (element); |
|
1551 |
|
1552 switch (transition) { |
|
1553 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
1554 if (!setup_source (decoder)) |
|
1555 goto source_failed; |
|
1556 break; |
|
1557 default: |
|
1558 break; |
|
1559 } |
|
1560 |
|
1561 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
|
1562 |
|
1563 switch (transition) { |
|
1564 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
1565 GST_DEBUG ("ready to paused"); |
|
1566 if (ret == GST_STATE_CHANGE_FAILURE) |
|
1567 goto setup_failed; |
|
1568 break; |
|
1569 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
1570 GST_DEBUG ("paused to ready"); |
|
1571 remove_decoders (decoder); |
|
1572 remove_pads (decoder); |
|
1573 remove_source (decoder); |
|
1574 break; |
|
1575 default: |
|
1576 break; |
|
1577 } |
|
1578 return ret; |
|
1579 |
|
1580 /* ERRORS */ |
|
1581 source_failed: |
|
1582 { |
|
1583 return GST_STATE_CHANGE_FAILURE; |
|
1584 } |
|
1585 setup_failed: |
|
1586 { |
|
1587 /* clean up leftover groups */ |
|
1588 return GST_STATE_CHANGE_FAILURE; |
|
1589 } |
|
1590 } |
|
1591 |
|
1592 gboolean gst_decode_bin_plugin_init (GstPlugin * plugin); |
|
1593 |
|
1594 static gboolean |
|
1595 gst_uri_decode_bin_plugin_init (GstPlugin * plugin) |
|
1596 { |
|
1597 GST_DEBUG_CATEGORY_INIT (gst_uri_decode_bin_debug, "uridecodebin", 0, |
|
1598 "URI decoder element"); |
|
1599 |
|
1600 #ifdef ENABLE_NLS |
|
1601 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, |
|
1602 LOCALEDIR); |
|
1603 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); |
|
1604 #endif /* ENABLE_NLS */ |
|
1605 |
|
1606 return gst_element_register (plugin, "uridecodebin", GST_RANK_NONE, |
|
1607 GST_TYPE_URI_DECODE_BIN); |
|
1608 } |
|
1609 |
|
1610 static gboolean |
|
1611 plugin_init (GstPlugin * plugin) |
|
1612 { |
|
1613 if (!gst_decode_bin_plugin_init (plugin)) |
|
1614 return FALSE; |
|
1615 if (!gst_uri_decode_bin_plugin_init (plugin)) |
|
1616 return FALSE; |
|
1617 |
|
1618 return TRUE; |
|
1619 } |
|
1620 |
|
1621 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
|
1622 GST_VERSION_MINOR, |
|
1623 "uridecodebin", |
|
1624 "URI Decoder bin", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, |
|
1625 GST_PACKAGE_ORIGIN); |
|
1626 |
|
1627 #ifdef __SYMBIAN32__ |
|
1628 EXPORT_C |
|
1629 #endif |
|
1630 GstPluginDesc* _GST_PLUGIN_DESC() |
|
1631 { |
|
1632 return &gst_plugin_desc; |
|
1633 } |