69 #define MIN_INT_8 ((gint8) (0x80)) |
61 #define MIN_INT_8 ((gint8) (0x80)) |
70 #define MIN_UINT_32 ((guint32)(0x00000000)) |
62 #define MIN_UINT_32 ((guint32)(0x00000000)) |
71 #define MIN_UINT_16 ((guint16)(0x0000)) |
63 #define MIN_UINT_16 ((guint16)(0x0000)) |
72 #define MIN_UINT_8 ((guint8) (0x00)) |
64 #define MIN_UINT_8 ((guint8) (0x00)) |
73 |
65 |
|
66 enum |
|
67 { |
|
68 PROP_0, |
|
69 PROP_FILTER_CAPS |
|
70 }; |
|
71 |
74 #define GST_CAT_DEFAULT gst_adder_debug |
72 #define GST_CAT_DEFAULT gst_adder_debug |
75 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); |
73 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); |
76 |
74 |
77 /* elementfactory information */ |
75 /* elementfactory information */ |
78 static const GstElementDetails adder_details = GST_ELEMENT_DETAILS ("Adder", |
76 |
79 "Generic/Audio", |
77 #define CAPS \ |
80 "Add N audio channels together", |
78 "audio/x-raw-int, " \ |
81 "Thomas <thomas@apestaart.org>"); |
79 "rate = (int) [ 1, MAX ], " \ |
|
80 "channels = (int) [ 1, MAX ], " \ |
|
81 "endianness = (int) BYTE_ORDER, " \ |
|
82 "width = (int) 32, " \ |
|
83 "depth = (int) 32, " \ |
|
84 "signed = (boolean) { true, false } ;" \ |
|
85 "audio/x-raw-int, " \ |
|
86 "rate = (int) [ 1, MAX ], " \ |
|
87 "channels = (int) [ 1, MAX ], " \ |
|
88 "endianness = (int) BYTE_ORDER, " \ |
|
89 "width = (int) 16, " \ |
|
90 "depth = (int) 16, " \ |
|
91 "signed = (boolean) { true, false } ;" \ |
|
92 "audio/x-raw-int, " \ |
|
93 "rate = (int) [ 1, MAX ], " \ |
|
94 "channels = (int) [ 1, MAX ], " \ |
|
95 "endianness = (int) BYTE_ORDER, " \ |
|
96 "width = (int) 8, " \ |
|
97 "depth = (int) 8, " \ |
|
98 "signed = (boolean) { true, false } ;" \ |
|
99 "audio/x-raw-float, " \ |
|
100 "rate = (int) [ 1, MAX ], " \ |
|
101 "channels = (int) [ 1, MAX ], " \ |
|
102 "endianness = (int) BYTE_ORDER, " \ |
|
103 "width = (int) { 32, 64 }" |
82 |
104 |
83 static GstStaticPadTemplate gst_adder_src_template = |
105 static GstStaticPadTemplate gst_adder_src_template = |
84 GST_STATIC_PAD_TEMPLATE ("src", |
106 GST_STATIC_PAD_TEMPLATE ("src", |
85 GST_PAD_SRC, |
107 GST_PAD_SRC, |
86 GST_PAD_ALWAYS, |
108 GST_PAD_ALWAYS, |
87 GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; " |
109 GST_STATIC_CAPS (CAPS) |
88 GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS) |
|
89 ); |
110 ); |
90 |
111 |
91 static GstStaticPadTemplate gst_adder_sink_template = |
112 static GstStaticPadTemplate gst_adder_sink_template = |
92 GST_STATIC_PAD_TEMPLATE ("sink%d", |
113 GST_STATIC_PAD_TEMPLATE ("sink%d", |
93 GST_PAD_SINK, |
114 GST_PAD_SINK, |
94 GST_PAD_REQUEST, |
115 GST_PAD_REQUEST, |
95 GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; " |
116 GST_STATIC_CAPS (CAPS) |
96 GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS) |
|
97 ); |
117 ); |
98 |
118 |
99 static void gst_adder_class_init (GstAdderClass * klass); |
119 static void gst_adder_class_init (GstAdderClass * klass); |
100 static void gst_adder_init (GstAdder * adder); |
120 static void gst_adder_init (GstAdder * adder); |
101 static void gst_adder_finalize (GObject * object); |
121 static void gst_adder_dispose (GObject * object); |
|
122 static void gst_adder_set_property (GObject * object, guint prop_id, |
|
123 const GValue * value, GParamSpec * pspec); |
|
124 static void gst_adder_get_property (GObject * object, guint prop_id, |
|
125 GValue * value, GParamSpec * pspec); |
102 |
126 |
103 static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps); |
127 static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps); |
104 static gboolean gst_adder_query (GstPad * pad, GstQuery * query); |
128 static gboolean gst_adder_query (GstPad * pad, GstQuery * query); |
105 static gboolean gst_adder_src_event (GstPad * pad, GstEvent * event); |
129 static gboolean gst_adder_src_event (GstPad * pad, GstEvent * event); |
106 static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event); |
130 static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event); |
140 "audio channel mixing element"); |
164 "audio channel mixing element"); |
141 } |
165 } |
142 return adder_type; |
166 return adder_type; |
143 } |
167 } |
144 |
168 |
145 /* clipping versions */ |
169 /* clipping versions (for int) |
|
170 * FIXME: what about: oil_add_s16 (out, out, in, bytes / sizeof (type)) |
|
171 */ |
146 #define MAKE_FUNC(name,type,ttype,min,max) \ |
172 #define MAKE_FUNC(name,type,ttype,min,max) \ |
147 static void name (type *out, type *in, gint bytes) { \ |
173 static void name (type *out, type *in, gint bytes) { \ |
148 gint i; \ |
174 gint i; \ |
149 for (i = 0; i < bytes / sizeof (type); i++) \ |
175 ttype add; \ |
150 out[i] = CLAMP ((ttype)out[i] + (ttype)in[i], min, max); \ |
176 for (i = 0; i < bytes / sizeof (type); i++) { \ |
|
177 add = (ttype)out[i] + (ttype)in[i]; \ |
|
178 out[i] = CLAMP (add, min, max); \ |
|
179 } \ |
|
180 } |
|
181 |
|
182 /* unsigned versions (for int) */ |
|
183 #define MAKE_FUNC_US(name,type,ttype,max) \ |
|
184 static void name (type *out, type *in, gint bytes) { \ |
|
185 gint i; \ |
|
186 ttype add; \ |
|
187 for (i = 0; i < bytes / sizeof (type); i++) { \ |
|
188 add = (ttype)out[i] + (ttype)in[i]; \ |
|
189 out[i] = ((add <= max) ? add : max); \ |
|
190 } \ |
151 } |
191 } |
152 |
192 |
153 /* non-clipping versions (for float) */ |
193 /* non-clipping versions (for float) */ |
154 #define MAKE_FUNC_NC(name,type,ttype) \ |
194 #define MAKE_FUNC_NC(name,type) \ |
155 static void name (type *out, type *in, gint bytes) { \ |
195 static void name (type *out, type *in, gint bytes) { \ |
156 gint i; \ |
196 gint i; \ |
157 for (i = 0; i < bytes / sizeof (type); i++) \ |
197 for (i = 0; i < bytes / sizeof (type); i++) \ |
158 out[i] = (ttype)out[i] + (ttype)in[i]; \ |
198 out[i] += in[i]; \ |
159 } |
199 } |
|
200 |
|
201 #if 0 |
|
202 /* right now, the liboil function don't seems to be faster on x86 |
|
203 * time gst-launch audiotestsrc num-buffers=50000 ! audio/x-raw-float ! adder name=m ! fakesink audiotestsrc num-buffers=50000 ! audio/x-raw-float ! m. |
|
204 * time gst-launch audiotestsrc num-buffers=50000 ! audio/x-raw-float,width=32 ! adder name=m ! fakesink audiotestsrc num-buffers=50000 ! audio/x-raw-float,width=32 ! m. |
|
205 */ |
|
206 static void |
|
207 add_float32 (gfloat * out, gfloat * in, gint bytes) |
|
208 { |
|
209 oil_add_f32 (out, out, in, bytes / sizeof (gfloat)); |
|
210 } |
|
211 |
|
212 static void |
|
213 add_float64 (gdouble * out, gdouble * in, gint bytes) |
|
214 { |
|
215 oil_add_f64 (out, out, in, bytes / sizeof (gdouble)); |
|
216 } |
|
217 #endif |
160 |
218 |
161 /* *INDENT-OFF* */ |
219 /* *INDENT-OFF* */ |
162 MAKE_FUNC (add_int32, gint32, gint64, MIN_INT_32, MAX_INT_32) |
220 MAKE_FUNC (add_int32, gint32, gint64, MIN_INT_32, MAX_INT_32) |
163 MAKE_FUNC (add_int16, gint16, gint32, MIN_INT_16, MAX_INT_16) |
221 MAKE_FUNC (add_int16, gint16, gint32, MIN_INT_16, MAX_INT_16) |
164 MAKE_FUNC (add_int8, gint8, gint16, MIN_INT_8, MAX_INT_8) |
222 MAKE_FUNC (add_int8, gint8, gint16, MIN_INT_8, MAX_INT_8) |
165 MAKE_FUNC (add_uint32, guint32, guint64, MIN_UINT_32, MAX_UINT_32) |
223 MAKE_FUNC_US (add_uint32, guint32, guint64, MAX_UINT_32) |
166 MAKE_FUNC (add_uint16, guint16, guint32, MIN_UINT_16, MAX_UINT_16) |
224 MAKE_FUNC_US (add_uint16, guint16, guint32, MAX_UINT_16) |
167 MAKE_FUNC (add_uint8, guint8, guint16, MIN_UINT_8, MAX_UINT_8) |
225 MAKE_FUNC_US (add_uint8, guint8, guint16, MAX_UINT_8) |
168 MAKE_FUNC_NC (add_float64, gdouble, gdouble) |
226 MAKE_FUNC_NC (add_float64, gdouble) |
169 MAKE_FUNC_NC (add_float32, gfloat, gfloat) |
227 MAKE_FUNC_NC (add_float32, gfloat) |
170 /* *INDENT-ON* */ |
228 /* *INDENT-ON* */ |
171 |
229 |
172 /* we can only accept caps that we and downstream can handle. */ |
230 /* we can only accept caps that we and downstream can handle. |
|
231 * if we have filtercaps set, use those to constrain the target caps. |
|
232 */ |
173 static GstCaps * |
233 static GstCaps * |
174 gst_adder_sink_getcaps (GstPad * pad) |
234 gst_adder_sink_getcaps (GstPad * pad) |
175 { |
235 { |
176 GstAdder *adder; |
236 GstAdder *adder; |
177 GstCaps *result, *peercaps, *sinkcaps; |
237 GstCaps *result, *peercaps, *sinkcaps; |
179 adder = GST_ADDER (GST_PAD_PARENT (pad)); |
239 adder = GST_ADDER (GST_PAD_PARENT (pad)); |
180 |
240 |
181 GST_OBJECT_LOCK (adder); |
241 GST_OBJECT_LOCK (adder); |
182 /* get the downstream possible caps */ |
242 /* get the downstream possible caps */ |
183 peercaps = gst_pad_peer_get_caps (adder->srcpad); |
243 peercaps = gst_pad_peer_get_caps (adder->srcpad); |
|
244 |
184 /* get the allowed caps on this sinkpad, we use the fixed caps function so |
245 /* get the allowed caps on this sinkpad, we use the fixed caps function so |
185 * that it does not call recursively in this function. */ |
246 * that it does not call recursively in this function. */ |
186 sinkcaps = gst_pad_get_fixed_caps_func (pad); |
247 sinkcaps = gst_pad_get_fixed_caps_func (pad); |
187 if (peercaps) { |
248 if (peercaps) { |
|
249 /* restrict with filter-caps if any */ |
|
250 if (adder->filter_caps) { |
|
251 result = gst_caps_intersect (peercaps, adder->filter_caps); |
|
252 gst_caps_unref (peercaps); |
|
253 peercaps = result; |
|
254 } |
188 /* if the peer has caps, intersect */ |
255 /* if the peer has caps, intersect */ |
189 GST_DEBUG_OBJECT (adder, "intersecting peer and template caps"); |
256 GST_DEBUG_OBJECT (adder, "intersecting peer and template caps"); |
190 result = gst_caps_intersect (peercaps, sinkcaps); |
257 result = gst_caps_intersect (peercaps, sinkcaps); |
191 gst_caps_unref (peercaps); |
258 gst_caps_unref (peercaps); |
192 gst_caps_unref (sinkcaps); |
259 gst_caps_unref (sinkcaps); |
233 |
303 |
234 /* parse caps now */ |
304 /* parse caps now */ |
235 structure = gst_caps_get_structure (caps, 0); |
305 structure = gst_caps_get_structure (caps, 0); |
236 media_type = gst_structure_get_name (structure); |
306 media_type = gst_structure_get_name (structure); |
237 if (strcmp (media_type, "audio/x-raw-int") == 0) { |
307 if (strcmp (media_type, "audio/x-raw-int") == 0) { |
238 GST_DEBUG_OBJECT (adder, "parse_caps sets adder to format int"); |
|
239 adder->format = GST_ADDER_FORMAT_INT; |
308 adder->format = GST_ADDER_FORMAT_INT; |
240 gst_structure_get_int (structure, "width", &adder->width); |
309 gst_structure_get_int (structure, "width", &adder->width); |
241 gst_structure_get_int (structure, "depth", &adder->depth); |
310 gst_structure_get_int (structure, "depth", &adder->depth); |
242 gst_structure_get_int (structure, "endianness", &adder->endianness); |
311 gst_structure_get_int (structure, "endianness", &adder->endianness); |
243 gst_structure_get_boolean (structure, "signed", &adder->is_signed); |
312 gst_structure_get_boolean (structure, "signed", &adder->is_signed); |
244 |
313 |
|
314 GST_INFO_OBJECT (pad, "parse_caps sets adder to format int, %d bit", |
|
315 adder->width); |
|
316 |
245 if (adder->endianness != G_BYTE_ORDER) |
317 if (adder->endianness != G_BYTE_ORDER) |
246 goto not_supported; |
318 goto not_supported; |
247 |
319 |
248 switch (adder->width) { |
320 switch (adder->width) { |
249 case 8: |
321 case 8: |
369 } |
451 } |
370 gst_iterator_free (it); |
452 gst_iterator_free (it); |
371 |
453 |
372 if (res) { |
454 if (res) { |
373 /* and store the max */ |
455 /* and store the max */ |
|
456 GST_DEBUG_OBJECT (adder, "Total duration in format %s: %" |
|
457 GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max)); |
374 gst_query_set_duration (query, format, max); |
458 gst_query_set_duration (query, format, max); |
|
459 } |
|
460 |
|
461 return res; |
|
462 } |
|
463 |
|
464 static gboolean |
|
465 gst_adder_query_latency (GstAdder * adder, GstQuery * query) |
|
466 { |
|
467 GstClockTime min, max; |
|
468 gboolean live; |
|
469 gboolean res; |
|
470 GstIterator *it; |
|
471 gboolean done; |
|
472 |
|
473 res = TRUE; |
|
474 done = FALSE; |
|
475 |
|
476 live = FALSE; |
|
477 min = 0; |
|
478 max = GST_CLOCK_TIME_NONE; |
|
479 |
|
480 /* Take maximum of all latency values */ |
|
481 it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder)); |
|
482 while (!done) { |
|
483 GstIteratorResult ires; |
|
484 |
|
485 gpointer item; |
|
486 |
|
487 ires = gst_iterator_next (it, &item); |
|
488 switch (ires) { |
|
489 case GST_ITERATOR_DONE: |
|
490 done = TRUE; |
|
491 break; |
|
492 case GST_ITERATOR_OK: |
|
493 { |
|
494 GstPad *pad = GST_PAD_CAST (item); |
|
495 GstQuery *peerquery; |
|
496 GstClockTime min_cur, max_cur; |
|
497 gboolean live_cur; |
|
498 |
|
499 peerquery = gst_query_new_latency (); |
|
500 |
|
501 /* Ask peer for latency */ |
|
502 res &= gst_pad_peer_query (pad, peerquery); |
|
503 |
|
504 /* take max from all valid return values */ |
|
505 if (res) { |
|
506 gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur); |
|
507 |
|
508 if (min_cur > min) |
|
509 min = min_cur; |
|
510 |
|
511 if (max_cur != GST_CLOCK_TIME_NONE && |
|
512 ((max != GST_CLOCK_TIME_NONE && max_cur > max) || |
|
513 (max == GST_CLOCK_TIME_NONE))) |
|
514 max = max_cur; |
|
515 |
|
516 live = live || live_cur; |
|
517 } |
|
518 |
|
519 gst_query_unref (peerquery); |
|
520 gst_object_unref (pad); |
|
521 break; |
|
522 } |
|
523 case GST_ITERATOR_RESYNC: |
|
524 live = FALSE; |
|
525 min = 0; |
|
526 max = GST_CLOCK_TIME_NONE; |
|
527 res = TRUE; |
|
528 gst_iterator_resync (it); |
|
529 break; |
|
530 default: |
|
531 res = FALSE; |
|
532 done = TRUE; |
|
533 break; |
|
534 } |
|
535 } |
|
536 gst_iterator_free (it); |
|
537 |
|
538 if (res) { |
|
539 /* store the results */ |
|
540 GST_DEBUG_OBJECT (adder, "Calculated total latency: live %s, min %" |
|
541 GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, |
|
542 (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max)); |
|
543 gst_query_set_latency (query, live, min, max); |
375 } |
544 } |
376 |
545 |
377 return res; |
546 return res; |
378 } |
547 } |
379 |
548 |
417 |
589 |
418 gst_object_unref (adder); |
590 gst_object_unref (adder); |
419 return res; |
591 return res; |
420 } |
592 } |
421 |
593 |
|
594 typedef struct |
|
595 { |
|
596 GstEvent *event; |
|
597 gboolean flush; |
|
598 } EventData; |
|
599 |
422 static gboolean |
600 static gboolean |
423 forward_event_func (GstPad * pad, GValue * ret, GstEvent * event) |
601 forward_event_func (GstPad * pad, GValue * ret, EventData * data) |
424 { |
602 { |
|
603 GstEvent *event = data->event; |
|
604 |
425 gst_event_ref (event); |
605 gst_event_ref (event); |
426 GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event)); |
606 GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event)); |
427 if (!gst_pad_push_event (pad, event)) { |
607 if (!gst_pad_push_event (pad, event)) { |
428 g_value_set_boolean (ret, FALSE); |
608 g_value_set_boolean (ret, FALSE); |
429 GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.", |
609 GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.", |
430 event, GST_EVENT_TYPE_NAME (event)); |
610 event, GST_EVENT_TYPE_NAME (event)); |
|
611 /* quick hack to unflush the pads, ideally we need a way to just unflush |
|
612 * this single collect pad */ |
|
613 if (data->flush) |
|
614 gst_pad_send_event (pad, gst_event_new_flush_stop ()); |
431 } else { |
615 } else { |
432 GST_LOG_OBJECT (pad, "Sent event %p (%s).", |
616 GST_LOG_OBJECT (pad, "Sent event %p (%s).", |
433 event, GST_EVENT_TYPE_NAME (event)); |
617 event, GST_EVENT_TYPE_NAME (event)); |
434 } |
618 } |
435 gst_object_unref (pad); |
619 gst_object_unref (pad); |
|
620 |
|
621 /* continue on other pads, even if one failed */ |
436 return TRUE; |
622 return TRUE; |
437 } |
623 } |
438 |
624 |
439 /* forwards the event to all sinkpads, takes ownership of the |
625 /* forwards the event to all sinkpads, takes ownership of the |
440 * event |
626 * event |
441 * |
627 * |
442 * Returns: TRUE if the event could be forwarded on all |
628 * Returns: TRUE if the event could be forwarded on all |
443 * sinkpads. |
629 * sinkpads. |
444 */ |
630 */ |
445 static gboolean |
631 static gboolean |
446 forward_event (GstAdder * adder, GstEvent * event) |
632 forward_event (GstAdder * adder, GstEvent * event, gboolean flush) |
447 { |
633 { |
448 gboolean ret; |
634 gboolean ret; |
449 GstIterator *it; |
635 GstIterator *it; |
|
636 GstIteratorResult ires; |
450 GValue vret = { 0 }; |
637 GValue vret = { 0 }; |
|
638 EventData data; |
451 |
639 |
452 GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event, |
640 GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event, |
453 GST_EVENT_TYPE_NAME (event)); |
641 GST_EVENT_TYPE_NAME (event)); |
454 |
642 |
455 ret = TRUE; |
643 ret = TRUE; |
|
644 data.event = event; |
|
645 data.flush = flush; |
456 |
646 |
457 g_value_init (&vret, G_TYPE_BOOLEAN); |
647 g_value_init (&vret, G_TYPE_BOOLEAN); |
458 g_value_set_boolean (&vret, TRUE); |
648 g_value_set_boolean (&vret, TRUE); |
459 it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder)); |
649 it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder)); |
460 gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret, |
650 while (TRUE) { |
461 event); |
651 ires = gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, |
|
652 &vret, &data); |
|
653 switch (ires) { |
|
654 case GST_ITERATOR_RESYNC: |
|
655 GST_WARNING ("resync"); |
|
656 gst_iterator_resync (it); |
|
657 g_value_set_boolean (&vret, TRUE); |
|
658 break; |
|
659 case GST_ITERATOR_OK: |
|
660 case GST_ITERATOR_DONE: |
|
661 ret = g_value_get_boolean (&vret); |
|
662 goto done; |
|
663 default: |
|
664 ret = FALSE; |
|
665 goto done; |
|
666 } |
|
667 } |
|
668 done: |
462 gst_iterator_free (it); |
669 gst_iterator_free (it); |
|
670 GST_LOG_OBJECT (adder, "Forwarded event %p (%s), ret=%d", event, |
|
671 GST_EVENT_TYPE_NAME (event), ret); |
463 gst_event_unref (event); |
672 gst_event_unref (event); |
464 |
|
465 ret = g_value_get_boolean (&vret); |
|
466 |
673 |
467 return ret; |
674 return ret; |
468 } |
675 } |
469 |
676 |
470 static gboolean |
677 static gboolean |
474 gboolean result; |
681 gboolean result; |
475 |
682 |
476 adder = GST_ADDER (gst_pad_get_parent (pad)); |
683 adder = GST_ADDER (gst_pad_get_parent (pad)); |
477 |
684 |
478 switch (GST_EVENT_TYPE (event)) { |
685 switch (GST_EVENT_TYPE (event)) { |
479 case GST_EVENT_QOS: |
|
480 /* QoS might be tricky */ |
|
481 result = FALSE; |
|
482 break; |
|
483 case GST_EVENT_SEEK: |
686 case GST_EVENT_SEEK: |
484 { |
687 { |
485 GstSeekFlags flags; |
688 GstSeekFlags flags; |
486 GstSeekType curtype; |
689 GstSeekType curtype; |
487 gint64 cur; |
690 gint64 cur; |
|
691 gboolean flush; |
488 |
692 |
489 /* parse the seek parameters */ |
693 /* parse the seek parameters */ |
490 gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype, |
694 gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype, |
491 &cur, NULL, NULL); |
695 &cur, NULL, NULL); |
492 |
696 |
|
697 flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; |
|
698 |
493 /* check if we are flushing */ |
699 /* check if we are flushing */ |
494 if (flags & GST_SEEK_FLAG_FLUSH) { |
700 if (flush) { |
495 /* make sure we accept nothing anymore and return WRONG_STATE */ |
701 /* make sure we accept nothing anymore and return WRONG_STATE */ |
496 gst_collect_pads_set_flushing (adder->collect, TRUE); |
702 gst_collect_pads_set_flushing (adder->collect, TRUE); |
497 |
703 |
498 /* flushing seek, start flush downstream, the flush will be done |
704 /* flushing seek, start flush downstream, the flush will be done |
499 * when all pads received a FLUSH_STOP. */ |
705 * when all pads received a FLUSH_STOP. */ |
500 gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ()); |
706 gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ()); |
501 } |
707 } |
|
708 GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event); |
502 |
709 |
503 /* now wait for the collected to be finished and mark a new |
710 /* now wait for the collected to be finished and mark a new |
504 * segment */ |
711 * segment. After we have the lock, no collect function is running and no |
|
712 * new collect function will be called for as long as we're flushing. */ |
505 GST_OBJECT_LOCK (adder->collect); |
713 GST_OBJECT_LOCK (adder->collect); |
506 if (curtype == GST_SEEK_TYPE_SET) |
714 if (curtype == GST_SEEK_TYPE_SET) |
507 adder->segment_position = cur; |
715 adder->segment_position = cur; |
508 else |
716 else |
509 adder->segment_position = 0; |
717 adder->segment_position = 0; |
|
718 /* make sure we push a new segment, to inform about new basetime |
|
719 * see FIXME in gst_adder_collected() */ |
510 adder->segment_pending = TRUE; |
720 adder->segment_pending = TRUE; |
|
721 if (flush) { |
|
722 /* Yes, we need to call _set_flushing again *WHEN* the streaming threads |
|
723 * have stopped so that the cookie gets properly updated. */ |
|
724 gst_collect_pads_set_flushing (adder->collect, TRUE); |
|
725 } |
|
726 /* we might have a pending flush_stop event now. This event will either be |
|
727 * sent by an upstream element when it completes the seek or we will push |
|
728 * one in the collected callback ourself */ |
|
729 adder->flush_stop_pending = flush; |
511 GST_OBJECT_UNLOCK (adder->collect); |
730 GST_OBJECT_UNLOCK (adder->collect); |
512 |
731 GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT, |
513 result = forward_event (adder, event); |
732 event); |
514 break; |
733 |
515 } |
734 result = forward_event (adder, event, flush); |
|
735 if (!result) { |
|
736 /* seek failed. maybe source is a live source. */ |
|
737 GST_DEBUG_OBJECT (adder, "seeking failed"); |
|
738 } |
|
739 /* FIXME: ideally we would like to send a flush-stop event from here but |
|
740 * collectpads does not have a method that allows us to do that. Instead |
|
741 * we forward all flush-stop events we receive on the sinkpads. We might |
|
742 * be sending too many flush-stop events. */ |
|
743 break; |
|
744 } |
|
745 case GST_EVENT_QOS: |
|
746 /* QoS might be tricky */ |
|
747 result = FALSE; |
|
748 break; |
516 case GST_EVENT_NAVIGATION: |
749 case GST_EVENT_NAVIGATION: |
517 /* navigation is rather pointless. */ |
750 /* navigation is rather pointless. */ |
518 result = FALSE; |
751 result = FALSE; |
519 break; |
752 break; |
520 default: |
753 default: |
521 /* just forward the rest for now */ |
754 /* just forward the rest for now */ |
522 result = forward_event (adder, event); |
755 GST_DEBUG_OBJECT (adder, "forward unhandled event: %s", |
|
756 GST_EVENT_TYPE_NAME (event)); |
|
757 result = forward_event (adder, event, FALSE); |
523 break; |
758 break; |
524 } |
759 } |
525 gst_object_unref (adder); |
760 gst_object_unref (adder); |
526 |
761 |
527 return result; |
762 return result; |
529 |
764 |
530 static gboolean |
765 static gboolean |
531 gst_adder_sink_event (GstPad * pad, GstEvent * event) |
766 gst_adder_sink_event (GstPad * pad, GstEvent * event) |
532 { |
767 { |
533 GstAdder *adder; |
768 GstAdder *adder; |
534 gboolean ret; |
769 gboolean ret = TRUE; |
535 |
770 |
536 adder = GST_ADDER (gst_pad_get_parent (pad)); |
771 adder = GST_ADDER (gst_pad_get_parent (pad)); |
537 |
772 |
538 GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), |
773 GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), |
539 GST_DEBUG_PAD_NAME (pad)); |
774 GST_DEBUG_PAD_NAME (pad)); |
540 |
775 |
541 switch (GST_EVENT_TYPE (event)) { |
776 switch (GST_EVENT_TYPE (event)) { |
542 case GST_EVENT_FLUSH_STOP: |
777 case GST_EVENT_FLUSH_STOP: |
543 /* mark a pending new segment. This event is synchronized |
778 /* we received a flush-stop. The collect_event function will push the |
544 * with the streaming thread so we can safely update the |
779 * event past our element. We simply forward all flush-stop events, even |
545 * variable without races. It's somewhat weird because we |
780 * when no flush-stop was pendingk, this is required because collectpads |
546 * assume the collectpads forwarded the FLUSH_STOP past us |
781 * does not provide an API to handle-but-not-forward the flush-stop. |
547 * and downstream (using our source pad, the bastard!). |
782 * We unset the pending flush-stop flag so that we don't send anymore |
|
783 * flush-stop from the collect function later. |
548 */ |
784 */ |
|
785 GST_OBJECT_LOCK (adder->collect); |
549 adder->segment_pending = TRUE; |
786 adder->segment_pending = TRUE; |
550 break; |
787 adder->flush_stop_pending = FALSE; |
|
788 /* Clear pending tags */ |
|
789 if (adder->pending_events) { |
|
790 g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL); |
|
791 g_list_free (adder->pending_events); |
|
792 adder->pending_events = NULL; |
|
793 } |
|
794 GST_OBJECT_UNLOCK (adder->collect); |
|
795 break; |
|
796 case GST_EVENT_TAG: |
|
797 GST_OBJECT_LOCK (adder->collect); |
|
798 /* collectpads is a pile of horse manure. */ |
|
799 adder->pending_events = g_list_append (adder->pending_events, event); |
|
800 GST_OBJECT_UNLOCK (adder->collect); |
|
801 goto beach; |
551 default: |
802 default: |
552 break; |
803 break; |
553 } |
804 } |
554 |
805 |
555 /* now GstCollectPads can take care of the rest, e.g. EOS */ |
806 /* now GstCollectPads can take care of the rest, e.g. EOS */ |
556 ret = adder->collect_event (pad, event); |
807 ret = adder->collect_event (pad, event); |
557 |
808 |
|
809 beach: |
558 gst_object_unref (adder); |
810 gst_object_unref (adder); |
559 return ret; |
811 return ret; |
560 } |
812 } |
561 |
813 |
562 static void |
814 static void |
563 gst_adder_class_init (GstAdderClass * klass) |
815 gst_adder_class_init (GstAdderClass * klass) |
564 { |
816 { |
565 GObjectClass *gobject_class; |
817 GObjectClass *gobject_class = (GObjectClass *) klass; |
566 GstElementClass *gstelement_class; |
818 GstElementClass *gstelement_class = (GstElementClass *) klass; |
567 |
819 |
568 gobject_class = (GObjectClass *) klass; |
820 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_adder_set_property); |
569 |
821 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_adder_get_property); |
570 gobject_class->finalize = gst_adder_finalize; |
822 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_adder_dispose); |
571 |
|
572 gstelement_class = (GstElementClass *) klass; |
|
573 |
823 |
574 gst_element_class_add_pad_template (gstelement_class, |
824 gst_element_class_add_pad_template (gstelement_class, |
575 gst_static_pad_template_get (&gst_adder_src_template)); |
825 gst_static_pad_template_get (&gst_adder_src_template)); |
576 gst_element_class_add_pad_template (gstelement_class, |
826 gst_element_class_add_pad_template (gstelement_class, |
577 gst_static_pad_template_get (&gst_adder_sink_template)); |
827 gst_static_pad_template_get (&gst_adder_sink_template)); |
578 gst_element_class_set_details (gstelement_class, &adder_details); |
828 gst_element_class_set_details_simple (gstelement_class, "Adder", |
|
829 "Generic/Audio", |
|
830 "Add N audio channels together", |
|
831 "Thomas Vander Stichele <thomas at apestaart dot org>"); |
579 |
832 |
580 parent_class = g_type_class_peek_parent (klass); |
833 parent_class = g_type_class_peek_parent (klass); |
581 |
834 |
582 gstelement_class->request_new_pad = gst_adder_request_new_pad; |
835 /** |
583 gstelement_class->release_pad = gst_adder_release_pad; |
836 * GstAdder:caps: |
584 gstelement_class->change_state = gst_adder_change_state; |
837 * |
|
838 * Since: 0.10.24 |
|
839 */ |
|
840 g_object_class_install_property (gobject_class, PROP_FILTER_CAPS, |
|
841 g_param_spec_boxed ("caps", "Target caps", |
|
842 "Set target format for mixing (NULL means ANY). " |
|
843 "Setting this property takes a reference to the supplied GstCaps " |
|
844 "object.", GST_TYPE_CAPS, |
|
845 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
|
846 |
|
847 gstelement_class->request_new_pad = |
|
848 GST_DEBUG_FUNCPTR (gst_adder_request_new_pad); |
|
849 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_adder_release_pad); |
|
850 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_adder_change_state); |
585 } |
851 } |
586 |
852 |
587 static void |
853 static void |
588 gst_adder_init (GstAdder * adder) |
854 gst_adder_init (GstAdder * adder) |
589 { |
855 { |
590 GstPadTemplate *template; |
856 GstPadTemplate *template; |
591 |
857 |
592 template = gst_static_pad_template_get (&gst_adder_src_template); |
858 template = gst_static_pad_template_get (&gst_adder_src_template); |
593 adder->srcpad = gst_pad_new_from_template (template, "src"); |
859 adder->srcpad = gst_pad_new_from_template (template, "src"); |
594 gst_object_unref (template); |
860 gst_object_unref (template); |
|
861 |
595 gst_pad_set_getcaps_function (adder->srcpad, |
862 gst_pad_set_getcaps_function (adder->srcpad, |
596 GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); |
863 GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); |
597 gst_pad_set_setcaps_function (adder->srcpad, |
864 gst_pad_set_setcaps_function (adder->srcpad, |
598 GST_DEBUG_FUNCPTR (gst_adder_setcaps)); |
865 GST_DEBUG_FUNCPTR (gst_adder_setcaps)); |
599 gst_pad_set_query_function (adder->srcpad, |
866 gst_pad_set_query_function (adder->srcpad, |
604 |
871 |
605 adder->format = GST_ADDER_FORMAT_UNSET; |
872 adder->format = GST_ADDER_FORMAT_UNSET; |
606 adder->padcount = 0; |
873 adder->padcount = 0; |
607 adder->func = NULL; |
874 adder->func = NULL; |
608 |
875 |
|
876 adder->filter_caps = gst_caps_new_any (); |
|
877 |
609 /* keep track of the sinkpads requested */ |
878 /* keep track of the sinkpads requested */ |
610 adder->collect = gst_collect_pads_new (); |
879 adder->collect = gst_collect_pads_new (); |
611 gst_collect_pads_set_function (adder->collect, |
880 gst_collect_pads_set_function (adder->collect, |
612 GST_DEBUG_FUNCPTR (gst_adder_collected), adder); |
881 GST_DEBUG_FUNCPTR (gst_adder_collected), adder); |
613 } |
882 } |
614 |
883 |
615 static void |
884 static void |
616 gst_adder_finalize (GObject * object) |
885 gst_adder_dispose (GObject * object) |
617 { |
886 { |
618 GstAdder *adder = GST_ADDER (object); |
887 GstAdder *adder = GST_ADDER (object); |
619 |
888 |
620 gst_object_unref (adder->collect); |
889 if (adder->collect) { |
621 adder->collect = NULL; |
890 gst_object_unref (adder->collect); |
622 |
891 adder->collect = NULL; |
623 G_OBJECT_CLASS (parent_class)->finalize (object); |
892 } |
624 } |
893 gst_caps_replace (&adder->filter_caps, NULL); |
|
894 if (adder->pending_events) { |
|
895 g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL); |
|
896 g_list_free (adder->pending_events); |
|
897 adder->pending_events = NULL; |
|
898 } |
|
899 |
|
900 G_OBJECT_CLASS (parent_class)->dispose (object); |
|
901 } |
|
902 |
|
903 static void |
|
904 gst_adder_set_property (GObject * object, guint prop_id, |
|
905 const GValue * value, GParamSpec * pspec) |
|
906 { |
|
907 GstAdder *adder = GST_ADDER (object); |
|
908 |
|
909 switch (prop_id) { |
|
910 case PROP_FILTER_CAPS:{ |
|
911 GstCaps *new_caps; |
|
912 GstCaps *old_caps; |
|
913 const GstCaps *new_caps_val = gst_value_get_caps (value); |
|
914 |
|
915 if (new_caps_val == NULL) { |
|
916 new_caps = gst_caps_new_any (); |
|
917 } else { |
|
918 new_caps = (GstCaps *) new_caps_val; |
|
919 gst_caps_ref (new_caps); |
|
920 } |
|
921 |
|
922 GST_OBJECT_LOCK (adder); |
|
923 old_caps = adder->filter_caps; |
|
924 adder->filter_caps = new_caps; |
|
925 GST_OBJECT_UNLOCK (adder); |
|
926 |
|
927 gst_caps_unref (old_caps); |
|
928 |
|
929 GST_DEBUG_OBJECT (adder, "set new caps %" GST_PTR_FORMAT, new_caps); |
|
930 break; |
|
931 } |
|
932 default: |
|
933 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
934 break; |
|
935 } |
|
936 } |
|
937 |
|
938 static void |
|
939 gst_adder_get_property (GObject * object, guint prop_id, GValue * value, |
|
940 GParamSpec * pspec) |
|
941 { |
|
942 GstAdder *adder = GST_ADDER (object); |
|
943 |
|
944 switch (prop_id) { |
|
945 case PROP_FILTER_CAPS: |
|
946 GST_OBJECT_LOCK (adder); |
|
947 gst_value_set_caps (value, adder->filter_caps); |
|
948 GST_OBJECT_UNLOCK (adder); |
|
949 break; |
|
950 default: |
|
951 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
952 break; |
|
953 } |
|
954 } |
|
955 |
625 |
956 |
626 static GstPad * |
957 static GstPad * |
627 gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ, |
958 gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ, |
628 const gchar * unused) |
959 const gchar * unused) |
629 { |
960 { |
692 |
1023 |
693 static GstFlowReturn |
1024 static GstFlowReturn |
694 gst_adder_collected (GstCollectPads * pads, gpointer user_data) |
1025 gst_adder_collected (GstCollectPads * pads, gpointer user_data) |
695 { |
1026 { |
696 /* |
1027 /* |
697 * combine channels by adding sample values |
1028 * combine streams by adding data values |
698 * basic algorithm : |
1029 * basic algorithm : |
699 * - this function is called when all pads have a buffer |
1030 * - this function is called when all pads have a buffer |
700 * - get available bytes on all pads. |
1031 * - get available bytes on all pads. |
701 * - repeat for each input pad : |
1032 * - repeat for each input pad : |
702 * - read available bytes, copy or add to target buffer |
1033 * - read available bytes, copy or add to target buffer |
703 * - if there's an EOS event, remove the input channel |
1034 * - if there's an EOS event, remove the input channel |
704 * - push out the output buffer |
1035 * - push out the output buffer |
|
1036 * |
|
1037 * todo: |
|
1038 * - would be nice to have a mixing mode, where instead of adding we mix |
|
1039 * - for float we could downscale after collect loop |
|
1040 * - for int we need to downscale each input to avoid clipping or |
|
1041 * mix into a temp (float) buffer and scale afterwards as well |
705 */ |
1042 */ |
706 GstAdder *adder; |
1043 GstAdder *adder; |
707 guint size; |
|
708 GSList *collected; |
1044 GSList *collected; |
709 GstBuffer *outbuf; |
|
710 GstFlowReturn ret; |
1045 GstFlowReturn ret; |
711 gpointer outbytes; |
1046 GstBuffer *outbuf = NULL; |
|
1047 gpointer outdata = NULL; |
|
1048 guint outsize; |
712 gboolean empty = TRUE; |
1049 gboolean empty = TRUE; |
713 |
1050 |
714 adder = GST_ADDER (user_data); |
1051 adder = GST_ADDER (user_data); |
715 |
1052 |
716 /* this is fatal */ |
1053 /* this is fatal */ |
717 if (G_UNLIKELY (adder->func == NULL)) |
1054 if (G_UNLIKELY (adder->func == NULL)) |
718 goto not_negotiated; |
1055 goto not_negotiated; |
719 |
1056 |
720 /* get available bytes for reading, this can be 0 which could mean |
1057 if (adder->flush_stop_pending) { |
721 * empty buffers or EOS, which we will catch when we loop over the |
1058 gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop ()); |
722 * pads. */ |
1059 adder->flush_stop_pending = FALSE; |
723 size = gst_collect_pads_available (pads); |
1060 } |
|
1061 |
|
1062 /* get available bytes for reading, this can be 0 which could mean empty |
|
1063 * buffers or EOS, which we will catch when we loop over the pads. */ |
|
1064 outsize = gst_collect_pads_available (pads); |
724 |
1065 |
725 GST_LOG_OBJECT (adder, |
1066 GST_LOG_OBJECT (adder, |
726 "starting to cycle through channels, %d bytes available (bps = %d)", size, |
1067 "starting to cycle through channels, %d bytes available (bps = %d)", |
727 adder->bps); |
1068 outsize, adder->bps); |
728 |
|
729 outbuf = NULL; |
|
730 outbytes = NULL; |
|
731 |
1069 |
732 for (collected = pads->data; collected; collected = g_slist_next (collected)) { |
1070 for (collected = pads->data; collected; collected = g_slist_next (collected)) { |
733 GstCollectData *data; |
1071 GstCollectData *collect_data; |
734 guint8 *bytes; |
|
735 guint len; |
|
736 GstBuffer *inbuf; |
1072 GstBuffer *inbuf; |
737 |
1073 guint8 *indata; |
738 data = (GstCollectData *) collected->data; |
1074 guint insize; |
|
1075 |
|
1076 collect_data = (GstCollectData *) collected->data; |
739 |
1077 |
740 /* get a subbuffer of size bytes */ |
1078 /* get a subbuffer of size bytes */ |
741 inbuf = gst_collect_pads_take_buffer (pads, data, size); |
1079 inbuf = gst_collect_pads_take_buffer (pads, collect_data, outsize); |
742 /* NULL means EOS or an empty buffer so we still need to flush in |
1080 /* NULL means EOS or an empty buffer so we still need to flush in |
743 * case of an empty buffer. */ |
1081 * case of an empty buffer. */ |
744 if (inbuf == NULL) { |
1082 if (inbuf == NULL) { |
745 GST_LOG_OBJECT (adder, "channel %p: no bytes available", data); |
1083 GST_LOG_OBJECT (adder, "channel %p: no bytes available", collect_data); |
746 goto next; |
1084 continue; |
747 } |
1085 } |
748 |
1086 |
749 bytes = GST_BUFFER_DATA (inbuf); |
1087 indata = GST_BUFFER_DATA (inbuf); |
750 len = GST_BUFFER_SIZE (inbuf); |
1088 insize = GST_BUFFER_SIZE (inbuf); |
751 |
1089 |
752 if (outbuf == NULL) { |
1090 if (outbuf == NULL) { |
753 GST_LOG_OBJECT (adder, "channel %p: making output buffer of %d bytes", |
1091 GST_LOG_OBJECT (adder, "channel %p: making output buffer of %d bytes", |
754 data, size); |
1092 collect_data, outsize); |
755 |
1093 |
756 /* first buffer, alloc size bytes. FIXME, we can easily subbuffer |
1094 /* first buffer, alloc outsize. |
757 * and _make_writable. */ |
1095 * FIXME: we can easily subbuffer and _make_writable. |
758 outbuf = gst_buffer_new_and_alloc (size); |
1096 * FIXME: only create empty buffer for first non-gap buffer, so that we |
759 outbytes = GST_BUFFER_DATA (outbuf); |
1097 * only use adder function when really adding |
|
1098 */ |
|
1099 outbuf = gst_buffer_new_and_alloc (outsize); |
|
1100 outdata = GST_BUFFER_DATA (outbuf); |
760 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (adder->srcpad)); |
1101 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (adder->srcpad)); |
761 |
1102 |
762 if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { |
1103 if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { |
|
1104 GST_LOG_OBJECT (adder, "channel %p: copying %d bytes from data %p", |
|
1105 collect_data, insize, indata); |
763 /* clear if we are only going to fill a partial buffer */ |
1106 /* clear if we are only going to fill a partial buffer */ |
764 if (G_UNLIKELY (size > len)) |
1107 if (G_UNLIKELY (outsize > insize)) |
765 memset (outbytes, 0, size); |
1108 memset ((guint8 *) outdata + insize, 0, outsize - insize); |
766 |
|
767 GST_LOG_OBJECT (adder, "channel %p: copying %d bytes from data %p", |
|
768 data, len, bytes); |
|
769 |
|
770 /* and copy the data into it */ |
1109 /* and copy the data into it */ |
771 memcpy (outbytes, bytes, len); |
1110 memcpy (outdata, indata, insize); |
772 empty = FALSE; |
1111 empty = FALSE; |
773 } else { |
1112 } else { |
|
1113 /* clear whole buffer */ |
774 GST_LOG_OBJECT (adder, "channel %p: zeroing %d bytes from data %p", |
1114 GST_LOG_OBJECT (adder, "channel %p: zeroing %d bytes from data %p", |
775 data, len, bytes); |
1115 collect_data, insize, indata); |
776 memset (outbytes, 0, size); |
1116 memset (outdata, 0, outsize); |
777 } |
1117 } |
778 } else { |
1118 } else { |
779 if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { |
1119 if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { |
780 GST_LOG_OBJECT (adder, "channel %p: mixing %d bytes from data %p", |
1120 GST_LOG_OBJECT (adder, "channel %p: mixing %d bytes from data %p", |
781 data, len, bytes); |
1121 collect_data, insize, indata); |
782 /* other buffers, need to add them */ |
1122 /* further buffers, need to add them */ |
783 adder->func ((gpointer) outbytes, (gpointer) bytes, len); |
1123 adder->func ((gpointer) outdata, (gpointer) indata, insize); |
784 empty = FALSE; |
1124 empty = FALSE; |
785 } else { |
1125 } else { |
786 GST_LOG_OBJECT (adder, "channel %p: skipping %d bytes from data %p", |
1126 GST_LOG_OBJECT (adder, "channel %p: skipping %d bytes from data %p", |
787 data, len, bytes); |
1127 collect_data, insize, indata); |
788 } |
1128 } |
789 } |
1129 } |
790 next: |
1130 gst_buffer_unref (inbuf); |
791 if (inbuf) |
|
792 gst_buffer_unref (inbuf); |
|
793 } |
1131 } |
794 |
1132 |
795 /* can only happen when no pads to collect or all EOS */ |
1133 /* can only happen when no pads to collect or all EOS */ |
796 if (outbuf == NULL) |
1134 if (outbuf == NULL) |
797 goto eos; |
1135 goto eos; |
816 * match. |
1154 * match. |
817 */ |
1155 */ |
818 event = gst_event_new_new_segment_full (FALSE, adder->segment_rate, |
1156 event = gst_event_new_new_segment_full (FALSE, adder->segment_rate, |
819 1.0, GST_FORMAT_TIME, adder->timestamp, -1, adder->segment_position); |
1157 1.0, GST_FORMAT_TIME, adder->timestamp, -1, adder->segment_position); |
820 |
1158 |
821 gst_pad_push_event (adder->srcpad, event); |
1159 if (event) { |
822 adder->segment_pending = FALSE; |
1160 if (!gst_pad_push_event (adder->srcpad, event)) { |
823 adder->segment_position = 0; |
1161 GST_WARNING_OBJECT (adder->srcpad, "Sending event %p (%s) failed.", |
|
1162 event, GST_EVENT_TYPE_NAME (event)); |
|
1163 } |
|
1164 adder->segment_pending = FALSE; |
|
1165 adder->segment_position = 0; |
|
1166 } else { |
|
1167 GST_WARNING_OBJECT (adder->srcpad, "Creating new segment event for " |
|
1168 "start:%" G_GINT64_FORMAT " pos:%" G_GINT64_FORMAT " failed", |
|
1169 adder->timestamp, adder->segment_position); |
|
1170 } |
|
1171 } |
|
1172 |
|
1173 if (G_UNLIKELY (adder->pending_events)) { |
|
1174 GList *tmp = adder->pending_events; |
|
1175 |
|
1176 while (tmp) { |
|
1177 GstEvent *ev = (GstEvent *) tmp->data; |
|
1178 |
|
1179 gst_pad_push_event (adder->srcpad, ev); |
|
1180 tmp = g_list_next (tmp); |
|
1181 } |
|
1182 g_list_free (adder->pending_events); |
|
1183 adder->pending_events = NULL; |
824 } |
1184 } |
825 |
1185 |
826 /* set timestamps on the output buffer */ |
1186 /* set timestamps on the output buffer */ |
827 GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp; |
1187 GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp; |
828 GST_BUFFER_OFFSET (outbuf) = adder->offset; |
1188 GST_BUFFER_OFFSET (outbuf) = adder->offset; |
829 |
1189 |
830 /* for the next timestamp, use the sample counter, which will |
1190 /* for the next timestamp, use the sample counter, which will |
831 * never accumulate rounding errors */ |
1191 * never accumulate rounding errors */ |
832 adder->offset += size / adder->bps; |
1192 adder->offset += outsize / adder->bps; |
833 adder->timestamp = gst_util_uint64_scale_int (adder->offset, |
1193 adder->timestamp = gst_util_uint64_scale_int (adder->offset, |
834 GST_SECOND, adder->rate); |
1194 GST_SECOND, adder->rate); |
835 |
1195 |
836 /* now we can set the duration of the buffer */ |
1196 /* now we can set the duration of the buffer */ |
837 GST_BUFFER_DURATION (outbuf) = adder->timestamp - |
1197 GST_BUFFER_DURATION (outbuf) = adder->timestamp - |