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