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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 /* TODO/FIXME:
21 *
22 * * BUFFERING MESSAGES
23 * ** How/Where do we deal with buffering messages from a new/prerolling
24 * source ? Ideally we want to re-use the same sourcebin ?
25 * ** Remember last buffering messages per source handler, if the SourceEntry
26 * group_id is the one being currently outputted on the source ghostpads,
27 * post the (last) buffering messages.
28 * If no group_id is being outputted (still prerolling), then output
29 * the messages directly
30 *
31 * * ASYNC HANDLING
32 * ** URIDECODEBIN3 is not async-aware.
33 *
34 * * GAPLESS HANDLING
35 * ** Correlate group_id and URI to know when/which stream is being outputted/started
36 */
37
38 /**
39 * SECTION:element-uridecodebin3
40 * @title: uridecodebin3
41 *
42 * Decodes data from a URI into raw media. It selects a source element that can
43 * handle the given #GstURIDecodeBin3:uri scheme and connects it to a decodebin.
44 */
45
46 #ifdef HAVE_CONFIG_H
47 # include "config.h"
48 #endif
49
50 #include <string.h>
51
52 #include <gst/gst.h>
53 #include <gst/gst-i18n-plugin.h>
54 #include <gst/pbutils/missing-plugins.h>
55
56 #include "gstplay-enum.h"
57 #include "gstrawcaps.h"
58 #include "gstplaybackelements.h"
59 #include "gstplaybackutils.h"
60
61 #define GST_TYPE_URI_DECODE_BIN3 \
62 (gst_uri_decode_bin3_get_type())
63 #define GST_URI_DECODE_BIN3(obj) \
64 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3))
65 #define GST_URI_DECODE_BIN3_CLASS(klass) \
66 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3Class))
67 #define GST_IS_URI_DECODE_BIN3(obj) \
68 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN3))
69 #define GST_IS_URI_DECODE_BIN3_CLASS(klass) \
70 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN3))
71 #define GST_URI_DECODE_BIN3_CAST(obj) ((GstURIDecodeBin3 *) (obj))
72
73 typedef struct _GstSourceGroup GstSourceGroup;
74 typedef struct _GstURIDecodeBin3 GstURIDecodeBin3;
75 typedef struct _GstURIDecodeBin3Class GstURIDecodeBin3Class;
76
77 #define GST_URI_DECODE_BIN3_LOCK(dec) (g_mutex_lock(&((GstURIDecodeBin3*)(dec))->lock))
78 #define GST_URI_DECODE_BIN3_UNLOCK(dec) (g_mutex_unlock(&((GstURIDecodeBin3*)(dec))->lock))
79
80 typedef struct _GstPlayItem GstPlayItem;
81 typedef struct _GstSourceItem GstSourceItem;
82 typedef struct _GstSourceHandler GstSourceHandler;
83 typedef struct _OutputPad OutputPad;
84
85 /* A structure describing a play item, which travels through the elements
86 * over time. */
87 struct _GstPlayItem
88 {
89 GstURIDecodeBin3 *uridecodebin;
90
91 /* Main URI */
92 GstSourceItem *main_item;
93
94 /* Auxiliary URI */
95 /* FIXME : Replace by a list later */
96 GstSourceItem *sub_item;
97
98 /* The group_id used to identify this play item via STREAM_START events
99 * This is the group_id which will be used externally (i.e. rewritten
100 * to outgoing STREAM_START events and in emitted signals).
101 * The urisourcebin-specific group_id is located in GstSourceItem */
102 guint group_id;
103
104 /* Is this play item the one being currently outputted by decodebin3
105 * and on our source ghostpads */
106 gboolean currently_outputted;
107 };
108
109 struct _GstSourceItem
110 {
111 /* The GstPlayItem to which this GstSourceItem belongs to */
112 GstPlayItem *play_item;
113
114 gchar *uri;
115
116 /* The urisourcebin controlling this uri
117 * Can be NULL */
118 GstSourceHandler *handler;
119
120 /* Last buffering information */
121 gint last_perc;
122 GstMessage *last_buffering_message;
123
124 /* The groupid created by urisourcebin for this uri */
125 guint internal_groupid;
126
127 /* FIXME : Add tag lists and other uri-specific items here ? */
128 };
129
130 /* Structure wrapping everything related to a urisourcebin */
131 struct _GstSourceHandler
132 {
133 GstURIDecodeBin3 *uridecodebin;
134
135 GstElement *urisourcebin;
136
137 /* Signal handlers */
138 gulong pad_added_id;
139 gulong pad_removed_id;
140 gulong source_setup_id;
141 gulong about_to_finish_id;
142
143 /* TRUE if the controlled urisourcebin was added to uridecodebin */
144 gboolean active;
145
146 /* whether urisourcebin is drained or not.
147 * Reset if/when setting a new URI */
148 gboolean drained;
149
150 /* Whether urisourcebin posted EOS on all pads and
151 * there is no pending entry */
152 gboolean is_eos;
153
154 /* TRUE if the urisourcebin handles main item */
155 gboolean is_main_source;
156
157 /* buffering message stored for after switching */
158 GstMessage *pending_buffering_msg;
159 };
160
161 /* Controls an output source pad */
162 struct _OutputPad
163 {
164 GstURIDecodeBin3 *uridecodebin;
165
166 GstPad *target_pad;
167 GstPad *ghost_pad;
168
169 /* Downstream event probe id */
170 gulong probe_id;
171
172 /* TRUE if the pad saw EOS. Reset to FALSE on STREAM_START */
173 gboolean is_eos;
174
175 /* The last seen (i.e. current) group_id
176 * Can be (guint)-1 if no group_id was seen yet */
177 guint current_group_id;
178 };
179
180 /**
181 * GstURIDecodeBin3
182 *
183 * uridecodebin3 element struct
184 */
185 struct _GstURIDecodeBin3
186 {
187 GstBin parent_instance;
188
189 GMutex lock; /* lock for constructing */
190
191 /* Properties */
192 GstElement *source;
193 guint64 connection_speed; /* In bits/sec (0 = unknown) */
194 GstCaps *caps;
195 guint64 buffer_duration; /* When buffering, buffer duration (ns) */
196 guint buffer_size; /* When buffering, buffer size (bytes) */
197 gboolean download;
198 gboolean use_buffering;
199 guint64 ring_buffer_max_size;
200
201 GList *play_items; /* List of GstPlayItem ordered by time of
202 * creation. Head of list is therefore the
203 * current (or pending if initial) one being
204 * outputted */
205 GstPlayItem *current; /* Currently active GstPlayItem. Can be NULL
206 * if no entry is active yet (i.e. no source
207 * pads) */
208
209 /* sources.
210 * FIXME : Replace by a more modular system later on */
211 GstSourceHandler *main_handler;
212 GstSourceHandler *sub_handler;
213
214 /* URI handling
215 * FIXME : Switch to a playlist-based API */
216 gchar *uri;
217 gboolean uri_changed; /* TRUE if uri changed */
218 gchar *suburi;
219 gboolean suburi_changed; /* TRUE if suburi changed */
220
221 /* A global decodebin3 that's used to actually do decoding */
222 GstElement *decodebin;
223
224 /* db3 signals */
225 gulong db_pad_added_id;
226 gulong db_pad_removed_id;
227 gulong db_select_stream_id;
228 gulong db_about_to_finish_id;
229
230 GList *output_pads; /* List of OutputPad */
231
232 GList *source_handlers; /* List of SourceHandler */
233
234 /* Whether we already signalled about-to-finish or not
235 * FIXME: Track this by group-id ! */
236 gboolean posted_about_to_finish;
237 };
238
239 static gint
gst_uridecodebin3_select_stream(GstURIDecodeBin3 * dbin,GstStreamCollection * collection,GstStream * stream)240 gst_uridecodebin3_select_stream (GstURIDecodeBin3 * dbin,
241 GstStreamCollection * collection, GstStream * stream)
242 {
243 GST_LOG_OBJECT (dbin, "default select-stream, returning -1");
244
245 return -1;
246 }
247
248 struct _GstURIDecodeBin3Class
249 {
250 GstBinClass parent_class;
251
252 gint (*select_stream) (GstURIDecodeBin3 * dbin,
253 GstStreamCollection * collection, GstStream * stream);
254 };
255
256 GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin3_debug);
257 #define GST_CAT_DEFAULT gst_uri_decode_bin3_debug
258
259 /* signals */
260 enum
261 {
262 SIGNAL_SELECT_STREAM,
263 SIGNAL_SOURCE_SETUP,
264 SIGNAL_ABOUT_TO_FINISH,
265 LAST_SIGNAL
266 };
267
268 #if 0
269 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
270 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
271 #endif
272
273 /* properties */
274 #define DEFAULT_PROP_URI NULL
275 #define DEFAULT_PROP_SUBURI NULL
276 #define DEFAULT_CONNECTION_SPEED 0
277 #define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps))
278 #define DEFAULT_BUFFER_DURATION -1
279 #define DEFAULT_BUFFER_SIZE -1
280 #define DEFAULT_DOWNLOAD FALSE
281 #define DEFAULT_USE_BUFFERING FALSE
282 #define DEFAULT_RING_BUFFER_MAX_SIZE 0
283
284 enum
285 {
286 PROP_0,
287 PROP_URI,
288 PROP_CURRENT_URI,
289 PROP_SUBURI,
290 PROP_CURRENT_SUBURI,
291 PROP_SOURCE,
292 PROP_CONNECTION_SPEED,
293 PROP_BUFFER_SIZE,
294 PROP_BUFFER_DURATION,
295 PROP_DOWNLOAD,
296 PROP_USE_BUFFERING,
297 PROP_RING_BUFFER_MAX_SIZE,
298 PROP_CAPS
299 };
300
301 static guint gst_uri_decode_bin3_signals[LAST_SIGNAL] = { 0 };
302
303 static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
304
305 static GstStaticPadTemplate video_src_template =
306 GST_STATIC_PAD_TEMPLATE ("video_%u",
307 GST_PAD_SRC,
308 GST_PAD_SOMETIMES,
309 GST_STATIC_CAPS_ANY);
310
311 static GstStaticPadTemplate audio_src_template =
312 GST_STATIC_PAD_TEMPLATE ("audio_%u",
313 GST_PAD_SRC,
314 GST_PAD_SOMETIMES,
315 GST_STATIC_CAPS_ANY);
316
317 static GstStaticPadTemplate text_src_template =
318 GST_STATIC_PAD_TEMPLATE ("text_%u",
319 GST_PAD_SRC,
320 GST_PAD_SOMETIMES,
321 GST_STATIC_CAPS_ANY);
322
323 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
324 GST_PAD_SRC,
325 GST_PAD_SOMETIMES,
326 GST_STATIC_CAPS_ANY);
327
328 GType gst_uri_decode_bin3_get_type (void);
329 #define gst_uri_decode_bin3_parent_class parent_class
330 G_DEFINE_TYPE (GstURIDecodeBin3, gst_uri_decode_bin3, GST_TYPE_BIN);
331
332 #define _do_init \
333 GST_DEBUG_CATEGORY_INIT (gst_uri_decode_bin3_debug, "uridecodebin3", 0, "URI decoder element 3"); \
334 playback_element_init (plugin);
335 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (uridecodebin3, "uridecodebin3",
336 GST_RANK_NONE, GST_TYPE_URI_DECODE_BIN3, _do_init);
337
338 #define REMOVE_SIGNAL(obj,id) \
339 if (id) { \
340 g_signal_handler_disconnect (obj, id); \
341 id = 0; \
342 }
343
344 static void gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
345 const GValue * value, GParamSpec * pspec);
346 static void gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
347 GValue * value, GParamSpec * pspec);
348 static void gst_uri_decode_bin3_finalize (GObject * obj);
349 static GstSourceHandler *new_source_handler (GstURIDecodeBin3 * uridecodebin,
350 gboolean is_main);
351
352 static GstStateChangeReturn gst_uri_decode_bin3_change_state (GstElement *
353 element, GstStateChange transition);
354 static gboolean gst_uri_decodebin3_send_event (GstElement * element,
355 GstEvent * event);
356
357 static gboolean
_gst_int_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)358 _gst_int_accumulator (GSignalInvocationHint * ihint,
359 GValue * return_accu, const GValue * handler_return, gpointer dummy)
360 {
361 gint res = g_value_get_int (handler_return);
362
363 g_value_set_int (return_accu, res);
364
365 if (res == -1)
366 return TRUE;
367
368 return FALSE;
369 }
370
371
372 static void
gst_uri_decode_bin3_class_init(GstURIDecodeBin3Class * klass)373 gst_uri_decode_bin3_class_init (GstURIDecodeBin3Class * klass)
374 {
375 GObjectClass *gobject_class;
376 GstElementClass *gstelement_class;
377
378 gobject_class = G_OBJECT_CLASS (klass);
379 gstelement_class = GST_ELEMENT_CLASS (klass);
380
381 gobject_class->set_property = gst_uri_decode_bin3_set_property;
382 gobject_class->get_property = gst_uri_decode_bin3_get_property;
383 gobject_class->finalize = gst_uri_decode_bin3_finalize;
384
385 g_object_class_install_property (gobject_class, PROP_URI,
386 g_param_spec_string ("uri", "URI", "URI to decode",
387 DEFAULT_PROP_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
388
389 g_object_class_install_property (gobject_class, PROP_CURRENT_URI,
390 g_param_spec_string ("current-uri", "Current URI",
391 "The currently playing URI", NULL,
392 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
393
394 g_object_class_install_property (gobject_class, PROP_SUBURI,
395 g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
396 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
397
398 g_object_class_install_property (gobject_class, PROP_CURRENT_SUBURI,
399 g_param_spec_string ("current-suburi", "Current .sub-URI",
400 "The currently playing URI of a subtitle",
401 NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
402
403 g_object_class_install_property (gobject_class, PROP_SOURCE,
404 g_param_spec_object ("source", "Source", "Source object used",
405 GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
406
407 g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
408 g_param_spec_uint64 ("connection-speed", "Connection Speed",
409 "Network connection speed in kbps (0 = unknown)",
410 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
411 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
412
413 g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
414 g_param_spec_int ("buffer-size", "Buffer size (bytes)",
415 "Buffer size when buffering streams (-1 default value)",
416 -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
417 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
418
419 g_object_class_install_property (gobject_class, PROP_BUFFER_DURATION,
420 g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
421 "Buffer duration when buffering streams (-1 default value)",
422 -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
423 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
424
425 /**
426 * GstURIDecodeBin3::download:
427 *
428 * For certain media type, enable download buffering.
429 */
430 g_object_class_install_property (gobject_class, PROP_DOWNLOAD,
431 g_param_spec_boolean ("download", "Download",
432 "Attempt download buffering when buffering network streams",
433 DEFAULT_DOWNLOAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
434 /**
435 * GstURIDecodeBin3::use-buffering:
436 *
437 * Emit BUFFERING messages based on low-/high-percent thresholds of the
438 * demuxed or parsed data.
439 * When download buffering is activated and used for the current media
440 * type, this property does nothing. Otherwise perform buffering on the
441 * demuxed or parsed media.
442 */
443 g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
444 g_param_spec_boolean ("use-buffering", "Use Buffering",
445 "Perform buffering on demuxed/parsed media",
446 DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
447
448 /**
449 * GstURIDecodeBin3::ring-buffer-max-size
450 *
451 * The maximum size of the ring buffer in kilobytes. If set to 0, the ring
452 * buffer is disabled. Default is 0.
453 */
454 g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
455 g_param_spec_uint64 ("ring-buffer-max-size",
456 "Max. ring buffer size (bytes)",
457 "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
458 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
459 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
460
461 g_object_class_install_property (gobject_class, PROP_CAPS,
462 g_param_spec_boxed ("caps", "Caps",
463 "The caps on which to stop decoding. (NULL = default)",
464 GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
465
466 /**
467 * GstURIDecodebin3::select-stream
468 * @decodebin: a #GstURIDecodebin3
469 * @collection: a #GstStreamCollection
470 * @stream: a #GstStream
471 *
472 * This signal is emitted whenever @decodebin needs to decide whether
473 * to expose a @stream of a given @collection.
474 *
475 * Note that the prefered way to select streams is to listen to
476 * GST_MESSAGE_STREAM_COLLECTION on the bus and send a
477 * GST_EVENT_SELECT_STREAMS with the streams the user wants.
478 *
479 * Returns: 1 if the stream should be selected, 0 if it shouldn't be selected.
480 * A value of -1 (default) lets @decodebin decide what to do with the stream.
481 * */
482 gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM] =
483 g_signal_new ("select-stream", G_TYPE_FROM_CLASS (klass),
484 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBin3Class, select_stream),
485 _gst_int_accumulator, NULL, NULL, G_TYPE_INT, 2,
486 GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
487
488 /**
489 * GstURIDecodeBin3::source-setup:
490 * @bin: the uridecodebin.
491 * @source: source element
492 *
493 * This signal is emitted after a source element has been created, so
494 * it can be configured by setting additional properties (e.g. set a
495 * proxy server for an http source, or set the device and read speed for
496 * an audio cd source).
497 */
498 gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP] =
499 g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
500 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
501 /**
502 * GstURIDecodeBin3::about-to-finish:
503 *
504 * This signal is emitted when the data for the selected URI is
505 * entirely buffered and it is safe to specify another URI.
506 */
507 gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH] =
508 g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
509 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE);
510
511
512 gst_element_class_add_static_pad_template (gstelement_class,
513 &video_src_template);
514 gst_element_class_add_static_pad_template (gstelement_class,
515 &audio_src_template);
516 gst_element_class_add_static_pad_template (gstelement_class,
517 &text_src_template);
518 gst_element_class_add_static_pad_template (gstelement_class, &src_template);
519 gst_element_class_set_static_metadata (gstelement_class,
520 "URI Decoder", "Generic/Bin/Decoder",
521 "Autoplug and decode an URI to raw media",
522 "Edward Hervey <edward@centricular.com>, Jan Schmidt <jan@centricular.com>");
523
524 gstelement_class->change_state = gst_uri_decode_bin3_change_state;
525 gstelement_class->send_event =
526 GST_DEBUG_FUNCPTR (gst_uri_decodebin3_send_event);
527
528 klass->select_stream = gst_uridecodebin3_select_stream;
529 }
530
531 static GstPadProbeReturn
db_src_probe(GstPad * pad,GstPadProbeInfo * info,OutputPad * output)532 db_src_probe (GstPad * pad, GstPadProbeInfo * info, OutputPad * output)
533 {
534 /* FIXME : IMPLEMENT */
535
536 /* EOS : Mark pad as EOS */
537
538 /* STREAM_START : Store group_id and check if currently active
539 * PlayEntry changed */
540
541 return GST_PAD_PROBE_OK;
542 }
543
544 static OutputPad *
add_output_pad(GstURIDecodeBin3 * dec,GstPad * target_pad)545 add_output_pad (GstURIDecodeBin3 * dec, GstPad * target_pad)
546 {
547 OutputPad *output;
548 gchar *pad_name;
549 GstEvent *stream_start;
550
551 output = g_slice_new0 (OutputPad);
552
553 GST_LOG_OBJECT (dec, "Created output %p", output);
554
555 output->uridecodebin = dec;
556 output->target_pad = target_pad;
557 output->current_group_id = (guint) - 1;
558 pad_name = gst_pad_get_name (target_pad);
559 output->ghost_pad = gst_ghost_pad_new (pad_name, target_pad);
560 g_free (pad_name);
561
562 gst_pad_set_active (output->ghost_pad, TRUE);
563
564 stream_start = gst_pad_get_sticky_event (target_pad,
565 GST_EVENT_STREAM_START, 0);
566 if (stream_start) {
567 gst_pad_store_sticky_event (output->ghost_pad, stream_start);
568 gst_event_unref (stream_start);
569 } else {
570 GST_WARNING_OBJECT (target_pad,
571 "Exposing pad without stored stream-start event");
572 }
573
574 gst_element_add_pad (GST_ELEMENT (dec), output->ghost_pad);
575
576 output->probe_id =
577 gst_pad_add_probe (output->target_pad,
578 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) db_src_probe,
579 output, NULL);
580
581 /* FIXME: LOCK TO PROTECT PAD LIST */
582 dec->output_pads = g_list_append (dec->output_pads, output);
583
584 return output;
585 }
586
587 static void
db_pad_added_cb(GstElement * element,GstPad * pad,GstURIDecodeBin3 * dec)588 db_pad_added_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
589 {
590 GST_DEBUG_OBJECT (dec, "Wrapping new pad %s:%s", GST_DEBUG_PAD_NAME (pad));
591
592 if (GST_PAD_IS_SRC (pad))
593 add_output_pad (dec, pad);
594 }
595
596 static void
db_pad_removed_cb(GstElement * element,GstPad * pad,GstURIDecodeBin3 * dec)597 db_pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
598 {
599 GList *tmp;
600 OutputPad *output = NULL;
601
602 if (!GST_PAD_IS_SRC (pad))
603 return;
604
605 GST_DEBUG_OBJECT (dec, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
606 /* FIXME: LOCK for list access */
607
608 for (tmp = dec->output_pads; tmp; tmp = tmp->next) {
609 OutputPad *cand = (OutputPad *) tmp->data;
610
611 if (cand->target_pad == pad) {
612 output = cand;
613 dec->output_pads = g_list_delete_link (dec->output_pads, tmp);
614 break;
615 }
616 }
617
618 if (output) {
619 GST_LOG_OBJECT (element, "Removing output %p", output);
620 /* Remove source ghost pad */
621 gst_ghost_pad_set_target ((GstGhostPad *) output->ghost_pad, NULL);
622 gst_element_remove_pad ((GstElement *) dec, output->ghost_pad);
623
624 /* FIXME : Update global/current PlayEntry group_id (did we switch ?) */
625
626 /* Remove event probe */
627 gst_pad_remove_probe (output->target_pad, output->probe_id);
628
629 g_slice_free (OutputPad, output);
630 }
631 }
632
633 static gint
db_select_stream_cb(GstElement * decodebin,GstStreamCollection * collection,GstStream * stream,GstURIDecodeBin3 * uridecodebin)634 db_select_stream_cb (GstElement * decodebin,
635 GstStreamCollection * collection, GstStream * stream,
636 GstURIDecodeBin3 * uridecodebin)
637 {
638 gint response = -1;
639
640 g_signal_emit (uridecodebin,
641 gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM], 0, collection, stream,
642 &response);
643 return response;
644 }
645
646 static void
db_about_to_finish_cb(GstElement * decodebin,GstURIDecodeBin3 * uridecodebin)647 db_about_to_finish_cb (GstElement * decodebin, GstURIDecodeBin3 * uridecodebin)
648 {
649 if (!uridecodebin->posted_about_to_finish) {
650 uridecodebin->posted_about_to_finish = TRUE;
651 g_signal_emit (uridecodebin,
652 gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
653 }
654 }
655
656 static void
gst_uri_decode_bin3_init(GstURIDecodeBin3 * dec)657 gst_uri_decode_bin3_init (GstURIDecodeBin3 * dec)
658 {
659 g_mutex_init (&dec->lock);
660
661 dec->uri = DEFAULT_PROP_URI;
662 dec->suburi = DEFAULT_PROP_SUBURI;
663 dec->connection_speed = DEFAULT_CONNECTION_SPEED;
664 dec->caps = DEFAULT_CAPS;
665 dec->buffer_duration = DEFAULT_BUFFER_DURATION;
666 dec->buffer_size = DEFAULT_BUFFER_SIZE;
667 dec->download = DEFAULT_DOWNLOAD;
668 dec->use_buffering = DEFAULT_USE_BUFFERING;
669 dec->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE;
670
671 dec->decodebin = gst_element_factory_make ("decodebin3", NULL);
672 gst_bin_add (GST_BIN_CAST (dec), dec->decodebin);
673 dec->db_pad_added_id =
674 g_signal_connect (dec->decodebin, "pad-added",
675 G_CALLBACK (db_pad_added_cb), dec);
676 dec->db_pad_removed_id =
677 g_signal_connect (dec->decodebin, "pad-removed",
678 G_CALLBACK (db_pad_removed_cb), dec);
679 dec->db_select_stream_id =
680 g_signal_connect (dec->decodebin, "select-stream",
681 G_CALLBACK (db_select_stream_cb), dec);
682 dec->db_about_to_finish_id =
683 g_signal_connect (dec->decodebin, "about-to-finish",
684 G_CALLBACK (db_about_to_finish_cb), dec);
685
686 GST_OBJECT_FLAG_SET (dec, GST_ELEMENT_FLAG_SOURCE);
687 gst_bin_set_suppressed_flags (GST_BIN (dec),
688 GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK);
689 }
690
691 static void
gst_uri_decode_bin3_finalize(GObject * obj)692 gst_uri_decode_bin3_finalize (GObject * obj)
693 {
694 GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (obj);
695
696 g_mutex_clear (&dec->lock);
697 g_free (dec->uri);
698 g_free (dec->suburi);
699
700 G_OBJECT_CLASS (parent_class)->finalize (obj);
701 }
702
703 static GstStateChangeReturn
activate_source_item(GstSourceItem * item)704 activate_source_item (GstSourceItem * item)
705 {
706 GstSourceHandler *handler = item->handler;
707
708 if (handler == NULL) {
709 GST_WARNING ("Can't activate item without a handler");
710 return GST_STATE_CHANGE_FAILURE;
711 }
712
713 g_object_set (handler->urisourcebin, "uri", item->uri, NULL);
714 if (!handler->active) {
715 gst_bin_add ((GstBin *) handler->uridecodebin, handler->urisourcebin);
716 /* if (!gst_element_sync_state_with_parent (handler->urisourcebin)) */
717 /* return GST_STATE_CHANGE_FAILURE; */
718 handler->active = TRUE;
719 }
720
721 return GST_STATE_CHANGE_SUCCESS;
722 }
723
724 static void
src_pad_added_cb(GstElement * element,GstPad * pad,GstSourceHandler * handler)725 src_pad_added_cb (GstElement * element, GstPad * pad,
726 GstSourceHandler * handler)
727 {
728 GstURIDecodeBin3 *uridecodebin;
729 GstPad *sinkpad = NULL;
730 GstPadLinkReturn res;
731 GstPlayItem *current_play_item;
732 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
733
734 uridecodebin = handler->uridecodebin;
735 current_play_item = uridecodebin->current;
736
737 GST_DEBUG_OBJECT (uridecodebin,
738 "New pad %" GST_PTR_FORMAT " from source %" GST_PTR_FORMAT, pad, element);
739
740 /* FIXME: Add probe to unify group_id and detect EOS */
741
742 /* Try to link to main sink pad only if it's from a main handler */
743 if (handler->is_main_source) {
744 sinkpad = gst_element_get_static_pad (uridecodebin->decodebin, "sink");
745 if (gst_pad_is_linked (sinkpad)) {
746 gst_object_unref (sinkpad);
747 sinkpad = NULL;
748 }
749 }
750
751 if (sinkpad == NULL)
752 sinkpad =
753 gst_element_request_pad_simple (uridecodebin->decodebin, "sink_%u");
754
755 if (sinkpad) {
756 GST_DEBUG_OBJECT (uridecodebin,
757 "Linking %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, pad, sinkpad);
758 res = gst_pad_link (pad, sinkpad);
759 gst_object_unref (sinkpad);
760 if (GST_PAD_LINK_FAILED (res))
761 goto link_failed;
762 }
763
764 /* Activate sub_item after the main source activation was finished */
765 if (handler->is_main_source && current_play_item->sub_item
766 && !current_play_item->sub_item->handler) {
767 current_play_item->sub_item->handler =
768 new_source_handler (uridecodebin, FALSE);
769 ret = activate_source_item (current_play_item->sub_item);
770 if (ret == GST_STATE_CHANGE_FAILURE)
771 goto sub_item_activation_failed;
772 }
773
774 return;
775
776 link_failed:
777 {
778 GST_ERROR_OBJECT (uridecodebin,
779 "failed to link pad %s:%s to decodebin, reason %s (%d)",
780 GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
781 return;
782 }
783 sub_item_activation_failed:
784 {
785 GST_ERROR_OBJECT (uridecodebin,
786 "failed to activate subtitle playback item");
787 return;
788 }
789 }
790
791 static void
src_pad_removed_cb(GstElement * element,GstPad * pad,GstSourceHandler * handler)792 src_pad_removed_cb (GstElement * element, GstPad * pad,
793 GstSourceHandler * handler)
794 {
795 GstURIDecodeBin3 *uridecodebin = handler->uridecodebin;
796 GstPad *peer_pad = gst_pad_get_peer (pad);
797
798 if (peer_pad) {
799 GstPadTemplate *templ = gst_pad_get_pad_template (peer_pad);
800
801 GST_DEBUG_OBJECT (uridecodebin,
802 "Source %" GST_PTR_FORMAT " removed pad %" GST_PTR_FORMAT " peer %"
803 GST_PTR_FORMAT, element, pad, peer_pad);
804
805 if (templ) {
806 if (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_REQUEST) {
807 GST_DEBUG_OBJECT (uridecodebin,
808 "Releasing decodebin pad %" GST_PTR_FORMAT, peer_pad);
809 gst_element_release_request_pad (uridecodebin->decodebin, peer_pad);
810 }
811 gst_object_unref (templ);
812 }
813
814 gst_object_unref (peer_pad);
815 }
816 }
817
818 static void
src_source_setup_cb(GstElement * element,GstElement * source,GstSourceHandler * handler)819 src_source_setup_cb (GstElement * element, GstElement * source,
820 GstSourceHandler * handler)
821 {
822 g_signal_emit (handler->uridecodebin,
823 gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP], 0, source, NULL);
824 }
825
826 static void
src_about_to_finish_cb(GstElement * element,GstSourceHandler * handler)827 src_about_to_finish_cb (GstElement * element, GstSourceHandler * handler)
828 {
829 /* FIXME : check if all sources are done */
830 if (!handler->uridecodebin->posted_about_to_finish) {
831 handler->uridecodebin->posted_about_to_finish = TRUE;
832 g_signal_emit (handler->uridecodebin,
833 gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
834 }
835 }
836
837 static GstSourceHandler *
new_source_handler(GstURIDecodeBin3 * uridecodebin,gboolean is_main)838 new_source_handler (GstURIDecodeBin3 * uridecodebin, gboolean is_main)
839 {
840 GstSourceHandler *handler;
841
842 handler = g_slice_new0 (GstSourceHandler);
843
844 handler->uridecodebin = uridecodebin;
845 handler->is_main_source = is_main;
846 handler->urisourcebin = gst_element_factory_make ("urisourcebin", NULL);
847 /* Set pending properties */
848 g_object_set (handler->urisourcebin,
849 "connection-speed", uridecodebin->connection_speed / 1000,
850 "download", uridecodebin->download,
851 "use-buffering", uridecodebin->use_buffering,
852 "buffer-duration", uridecodebin->buffer_duration,
853 "buffer-size", uridecodebin->buffer_size,
854 "ring-buffer-max-size", uridecodebin->ring_buffer_max_size, NULL);
855
856 handler->pad_added_id =
857 g_signal_connect (handler->urisourcebin, "pad-added",
858 (GCallback) src_pad_added_cb, handler);
859 handler->pad_removed_id =
860 g_signal_connect (handler->urisourcebin, "pad-removed",
861 (GCallback) src_pad_removed_cb, handler);
862 handler->source_setup_id =
863 g_signal_connect (handler->urisourcebin, "source-setup",
864 (GCallback) src_source_setup_cb, handler);
865 handler->about_to_finish_id =
866 g_signal_connect (handler->urisourcebin, "about-to-finish",
867 (GCallback) src_about_to_finish_cb, handler);
868
869 uridecodebin->source_handlers =
870 g_list_append (uridecodebin->source_handlers, handler);
871
872 return handler;
873 }
874
875 static void
gst_uri_decode_bin3_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)876 gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
877 const GValue * value, GParamSpec * pspec)
878 {
879 GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
880
881 switch (prop_id) {
882 case PROP_URI:
883 if (dec->uri)
884 g_free (dec->uri);
885 dec->uri = g_value_dup_string (value);
886 break;
887 case PROP_SUBURI:
888 if (dec->suburi)
889 g_free (dec->suburi);
890 dec->suburi = g_value_dup_string (value);
891 break;
892 case PROP_CONNECTION_SPEED:
893 GST_URI_DECODE_BIN3_LOCK (dec);
894 dec->connection_speed = g_value_get_uint64 (value) * 1000;
895 GST_URI_DECODE_BIN3_UNLOCK (dec);
896 break;
897 case PROP_BUFFER_SIZE:
898 dec->buffer_size = g_value_get_int (value);
899 break;
900 case PROP_BUFFER_DURATION:
901 dec->buffer_duration = g_value_get_int64 (value);
902 break;
903 case PROP_DOWNLOAD:
904 dec->download = g_value_get_boolean (value);
905 break;
906 case PROP_USE_BUFFERING:
907 dec->use_buffering = g_value_get_boolean (value);
908 break;
909 case PROP_RING_BUFFER_MAX_SIZE:
910 dec->ring_buffer_max_size = g_value_get_uint64 (value);
911 break;
912 case PROP_CAPS:
913 GST_OBJECT_LOCK (dec);
914 if (dec->caps)
915 gst_caps_unref (dec->caps);
916 dec->caps = g_value_dup_boxed (value);
917 GST_OBJECT_UNLOCK (dec);
918 break;
919 default:
920 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
921 break;
922 }
923 }
924
925 static void
gst_uri_decode_bin3_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)926 gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
927 GValue * value, GParamSpec * pspec)
928 {
929 GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
930
931 switch (prop_id) {
932 case PROP_URI:
933 {
934 g_value_set_string (value, dec->uri);
935 break;
936 }
937 case PROP_CURRENT_URI:
938 {
939 if (dec->current && dec->current->main_item) {
940 g_value_set_string (value, dec->current->main_item->uri);
941 } else {
942 g_value_set_string (value, NULL);
943 }
944 break;
945 }
946 case PROP_SUBURI:
947 {
948 g_value_set_string (value, dec->suburi);
949 break;
950 }
951 case PROP_CURRENT_SUBURI:
952 {
953 if (dec->current && dec->current->sub_item) {
954 g_value_set_string (value, dec->current->sub_item->uri);
955 } else {
956 g_value_set_string (value, NULL);
957 }
958 break;
959 }
960 case PROP_SOURCE:
961 {
962 GST_OBJECT_LOCK (dec);
963 g_value_set_object (value, dec->source);
964 GST_OBJECT_UNLOCK (dec);
965 break;
966 }
967 case PROP_CONNECTION_SPEED:
968 GST_URI_DECODE_BIN3_LOCK (dec);
969 g_value_set_uint64 (value, dec->connection_speed / 1000);
970 GST_URI_DECODE_BIN3_UNLOCK (dec);
971 break;
972 case PROP_BUFFER_SIZE:
973 GST_OBJECT_LOCK (dec);
974 g_value_set_int (value, dec->buffer_size);
975 GST_OBJECT_UNLOCK (dec);
976 break;
977 case PROP_BUFFER_DURATION:
978 GST_OBJECT_LOCK (dec);
979 g_value_set_int64 (value, dec->buffer_duration);
980 GST_OBJECT_UNLOCK (dec);
981 break;
982 case PROP_DOWNLOAD:
983 g_value_set_boolean (value, dec->download);
984 break;
985 case PROP_USE_BUFFERING:
986 g_value_set_boolean (value, dec->use_buffering);
987 break;
988 case PROP_RING_BUFFER_MAX_SIZE:
989 g_value_set_uint64 (value, dec->ring_buffer_max_size);
990 break;
991 case PROP_CAPS:
992 GST_OBJECT_LOCK (dec);
993 g_value_set_boxed (value, dec->caps);
994 GST_OBJECT_UNLOCK (dec);
995 break;
996 default:
997 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
998 break;
999 }
1000 }
1001
1002 static void
free_source_handler(GstURIDecodeBin3 * uridecodebin,GstSourceHandler * handler)1003 free_source_handler (GstURIDecodeBin3 * uridecodebin,
1004 GstSourceHandler * handler)
1005 {
1006 GST_LOG_OBJECT (uridecodebin, "source handler %p", handler);
1007 if (handler->active) {
1008 GST_LOG_OBJECT (uridecodebin, "Removing %" GST_PTR_FORMAT,
1009 handler->urisourcebin);
1010 gst_element_set_state (handler->urisourcebin, GST_STATE_NULL);
1011 gst_bin_remove ((GstBin *) uridecodebin, handler->urisourcebin);
1012 }
1013 uridecodebin->source_handlers =
1014 g_list_remove (uridecodebin->source_handlers, handler);
1015 g_slice_free (GstSourceHandler, handler);
1016 }
1017
1018 static GstSourceItem *
new_source_item(GstURIDecodeBin3 * dec,GstPlayItem * item,gchar * uri)1019 new_source_item (GstURIDecodeBin3 * dec, GstPlayItem * item, gchar * uri)
1020 {
1021 GstSourceItem *sourceitem = g_slice_new0 (GstSourceItem);
1022
1023 sourceitem->play_item = item;
1024 sourceitem->uri = uri;
1025
1026 return sourceitem;
1027 }
1028
1029 static void
free_source_item(GstURIDecodeBin3 * uridecodebin,GstSourceItem * item)1030 free_source_item (GstURIDecodeBin3 * uridecodebin, GstSourceItem * item)
1031 {
1032 GST_LOG_OBJECT (uridecodebin, "source item %p", item);
1033 if (item->handler)
1034 free_source_handler (uridecodebin, item->handler);
1035 g_slice_free (GstSourceItem, item);
1036 }
1037
1038 static GstPlayItem *
new_play_item(GstURIDecodeBin3 * dec,gchar * uri,gchar * suburi)1039 new_play_item (GstURIDecodeBin3 * dec, gchar * uri, gchar * suburi)
1040 {
1041 GstPlayItem *item = g_slice_new0 (GstPlayItem);
1042
1043 item->uridecodebin = dec;
1044 item->main_item = new_source_item (dec, item, uri);
1045 if (suburi)
1046 item->sub_item = new_source_item (dec, item, suburi);
1047
1048 return item;
1049 }
1050
1051 static void
free_play_item(GstURIDecodeBin3 * dec,GstPlayItem * item)1052 free_play_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
1053 {
1054 GST_LOG_OBJECT (dec, "play item %p", item);
1055 if (item->main_item)
1056 free_source_item (dec, item->main_item);
1057 if (item->sub_item)
1058 free_source_item (dec, item->sub_item);
1059
1060 g_slice_free (GstPlayItem, item);
1061 }
1062
1063 /* Sync source handlers for the given play item. Might require creating/removing some
1064 * and/or configure the handlers accordingly */
1065 static GstStateChangeReturn
assign_handlers_to_item(GstURIDecodeBin3 * dec,GstPlayItem * item)1066 assign_handlers_to_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
1067 {
1068 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1069
1070 /* FIXME : Go over existing handlers to see if we can assign some to the
1071 * given item */
1072
1073 /* Create missing handlers */
1074 if (item->main_item->handler == NULL) {
1075 item->main_item->handler = new_source_handler (dec, TRUE);
1076 ret = activate_source_item (item->main_item);
1077 if (ret == GST_STATE_CHANGE_FAILURE)
1078 return ret;
1079 }
1080
1081 return ret;
1082 }
1083
1084 /* Called to activate the next play item */
1085 static GstStateChangeReturn
activate_next_play_item(GstURIDecodeBin3 * dec)1086 activate_next_play_item (GstURIDecodeBin3 * dec)
1087 {
1088 GstPlayItem *item;
1089 GstStateChangeReturn ret;
1090
1091 /* If there is no current play entry, create one from the uri/suburi
1092 * FIXME : Use a playlist API in the future */
1093 item = new_play_item (dec, dec->uri, dec->suburi);
1094
1095 ret = assign_handlers_to_item (dec, item);
1096 if (ret == GST_STATE_CHANGE_FAILURE) {
1097 free_play_item (dec, item);
1098 return ret;
1099 }
1100
1101 dec->play_items = g_list_append (dec->play_items, item);
1102 dec->current = dec->play_items->data;
1103
1104 return ret;
1105 }
1106
1107 static void
free_play_items(GstURIDecodeBin3 * dec)1108 free_play_items (GstURIDecodeBin3 * dec)
1109 {
1110 GList *tmp;
1111
1112 for (tmp = dec->play_items; tmp; tmp = tmp->next) {
1113 GstPlayItem *item = (GstPlayItem *) tmp->data;
1114 free_play_item (dec, item);
1115 }
1116
1117 g_list_free (dec->play_items);
1118 dec->play_items = NULL;
1119 dec->current = NULL;
1120 }
1121
1122 static GstStateChangeReturn
gst_uri_decode_bin3_change_state(GstElement * element,GstStateChange transition)1123 gst_uri_decode_bin3_change_state (GstElement * element,
1124 GstStateChange transition)
1125 {
1126 GstStateChangeReturn ret;
1127 GstURIDecodeBin3 *uridecodebin = (GstURIDecodeBin3 *) element;
1128
1129 switch (transition) {
1130 case GST_STATE_CHANGE_NULL_TO_READY:
1131 g_object_set (uridecodebin->decodebin, "caps", uridecodebin->caps, NULL);
1132 break;
1133 case GST_STATE_CHANGE_READY_TO_PAUSED:
1134 ret = activate_next_play_item (uridecodebin);
1135 if (ret == GST_STATE_CHANGE_FAILURE)
1136 goto failure;
1137 default:
1138 break;
1139 }
1140
1141 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1142 if (ret == GST_STATE_CHANGE_FAILURE)
1143 goto failure;
1144
1145 switch (transition) {
1146 case GST_STATE_CHANGE_PAUSED_TO_READY:
1147 /* FIXME: Cleanup everything */
1148 free_play_items (uridecodebin);
1149 /* Free play item */
1150 uridecodebin->posted_about_to_finish = FALSE;
1151 break;
1152 default:
1153 break;
1154 }
1155
1156 return ret;
1157
1158 /* ERRORS */
1159 failure:
1160 {
1161 if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
1162 free_play_items (uridecodebin);
1163 return ret;
1164 }
1165 }
1166
1167 static gboolean
gst_uri_decodebin3_send_event(GstElement * element,GstEvent * event)1168 gst_uri_decodebin3_send_event (GstElement * element, GstEvent * event)
1169 {
1170 GstURIDecodeBin3 *self = GST_URI_DECODE_BIN3 (element);
1171
1172 if (GST_EVENT_IS_UPSTREAM (event) && self->decodebin)
1173 return gst_element_send_event (self->decodebin, event);
1174
1175 return GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
1176 }
1177