static gboolean gst_adder_sink_event (GstCollectPads2 * pads, GstCollectData2 * pad, GstEvent * event, gpointer user_data) { GstAdder *adder = GST_ADDER (user_data); gboolean res = FALSE; GST_DEBUG_OBJECT (pad->pad, "Got %s event on sink pad from %s", GST_EVENT_TYPE_NAME (event), GST_OBJECT_NAME (GST_EVENT_SRC (event))); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: /* drop flush start events, as we forwarded one already when handing the * flushing seek on the sink pad */ gst_event_unref (event); res = TRUE; break; case GST_EVENT_FLUSH_STOP: /* we received a flush-stop. We will only forward it when * flush_stop_pending is set, and we will unset it then. */ if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending, TRUE, FALSE)) { g_atomic_int_set (&adder->new_segment_pending, TRUE); GST_DEBUG_OBJECT (pad->pad, "forwarding flush stop"); } else { gst_event_unref (event); res = TRUE; GST_DEBUG_OBJECT (pad->pad, "eating flush stop"); } /* Clear pending tags */ if (adder->pending_events) { g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL); g_list_free (adder->pending_events); adder->pending_events = NULL; } break; case GST_EVENT_TAG: /* collect tags here so we can push them out when we collect data */ adder->pending_events = g_list_append (adder->pending_events, event); res = TRUE; break; case GST_EVENT_NEWSEGMENT: if (g_atomic_int_compare_and_exchange (&adder->wait_for_new_segment, TRUE, FALSE)) { /* make sure we push a new segment, to inform about new basetime * see FIXME in gst_adder_collected() */ g_atomic_int_set (&adder->new_segment_pending, TRUE); } gst_event_unref (event); res = TRUE; break; default: break; } return res; }
/* we can only accept caps that we and downstream can handle. * if we have filtercaps set, use those to constrain the target caps. */ static GstCaps * gst_adder_sink_getcaps (GstPad * pad) { GstAdder *adder; GstCaps *result, *peercaps, *sinkcaps, *filter_caps; adder = GST_ADDER (GST_PAD_PARENT (pad)); GST_OBJECT_LOCK (adder); /* take filter */ if ((filter_caps = adder->filter_caps)) gst_caps_ref (filter_caps); GST_OBJECT_UNLOCK (adder); /* get the downstream possible caps */ peercaps = gst_pad_peer_get_caps (adder->srcpad); /* get the allowed caps on this sinkpad, we use the fixed caps function so * that it does not call recursively in this function. */ sinkcaps = gst_pad_get_fixed_caps_func (pad); if (peercaps) { /* restrict with filter-caps if any */ if (filter_caps) { GST_DEBUG_OBJECT (adder, "filtering peer caps"); result = gst_caps_intersect (peercaps, filter_caps); gst_caps_unref (peercaps); peercaps = result; } /* if the peer has caps, intersect */ GST_DEBUG_OBJECT (adder, "intersecting peer and template caps"); result = gst_caps_intersect (peercaps, sinkcaps); gst_caps_unref (peercaps); gst_caps_unref (sinkcaps); } else { /* the peer has no caps (or there is no peer), just use the allowed caps * of this sinkpad. */ /* restrict with filter-caps if any */ if (filter_caps) { GST_DEBUG_OBJECT (adder, "no peer caps, using filtered sinkcaps"); result = gst_caps_intersect (sinkcaps, filter_caps); gst_caps_unref (sinkcaps); } else { GST_DEBUG_OBJECT (adder, "no peer caps, using sinkcaps"); result = sinkcaps; } } if (filter_caps) gst_caps_unref (filter_caps); GST_LOG_OBJECT (adder, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, pad, GST_PAD_NAME (pad), result); return result; }
static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event) { GstAdder *adder; gboolean ret = TRUE; adder = GST_ADDER (gst_pad_get_parent (pad)); GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: /* we received a flush-stop. The collect_event function will push the * event past our element. We simply forward all flush-stop events, even * when no flush-stop was pending, this is required because collectpads * does not provide an API to handle-but-not-forward the flush-stop. * We unset the pending flush-stop flag so that we don't send anymore * flush-stop from the collect function later. */ GST_OBJECT_LOCK (adder->collect); adder->segment_pending = TRUE; adder->flush_stop_pending = FALSE; /* Clear pending tags */ if (adder->pending_events) { g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL); g_list_free (adder->pending_events); adder->pending_events = NULL; } GST_OBJECT_UNLOCK (adder->collect); break; case GST_EVENT_TAG: GST_OBJECT_LOCK (adder->collect); /* collect tags here so we can push them out when we collect data */ adder->pending_events = g_list_append (adder->pending_events, event); GST_OBJECT_UNLOCK (adder->collect); goto beach; default: break; } /* now GstCollectPads can take care of the rest, e.g. EOS */ ret = adder->collect_event (pad, event); beach: gst_object_unref (adder); return ret; }
static gboolean gst_adder_query (GstPad * pad, GstQuery * query) { GstAdder *adder = GST_ADDER (gst_pad_get_parent (pad)); gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: { GstFormat format; gst_query_parse_position (query, &format, NULL); switch (format) { case GST_FORMAT_TIME: /* FIXME, bring to stream time, might be tricky */ gst_query_set_position (query, format, adder->timestamp); res = TRUE; break; case GST_FORMAT_DEFAULT: gst_query_set_position (query, format, adder->offset); res = TRUE; break; default: break; } break; } case GST_QUERY_DURATION: res = gst_adder_query_duration (adder, query); break; case GST_QUERY_LATENCY: res = gst_adder_query_latency (adder, query); break; default: /* FIXME, needs a custom query handler because we have multiple * sinkpads */ res = gst_pad_query_default (pad, query); break; } gst_object_unref (adder); return res; }
static gboolean gst_adder_src_event (GstPad * pad, GstEvent * event) { GstAdder *adder; gboolean result; adder = GST_ADDER (gst_pad_get_parent (pad)); GST_DEBUG_OBJECT (pad, "Got %s event on src pad from %s", GST_EVENT_TYPE_NAME (event), GST_OBJECT_NAME (GST_EVENT_SRC (event))); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { GstSeekFlags flags; GstSeekType curtype, endtype; gint64 cur, end; gboolean flush; /* parse the seek parameters */ gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype, &cur, &endtype, &end); if ((curtype != GST_SEEK_TYPE_NONE) && (curtype != GST_SEEK_TYPE_SET)) { result = FALSE; GST_DEBUG_OBJECT (adder, "seeking failed, unhandled seek type for start: %d", curtype); goto done; } if ((endtype != GST_SEEK_TYPE_NONE) && (endtype != GST_SEEK_TYPE_SET)) { result = FALSE; GST_DEBUG_OBJECT (adder, "seeking failed, unhandled seek type for end: %d", endtype); goto done; } flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; /* check if we are flushing */ if (flush) { /* flushing seek, start flush downstream, the flush will be done * when all pads received a FLUSH_STOP. * Make sure we accept nothing anymore and return WRONG_STATE. * We send a flush-start before, to ensure no streaming is done * as we need to take the stream lock. */ gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ()); gst_collect_pads2_set_flushing (adder->collect, TRUE); /* We can't send FLUSH_STOP here since upstream could start pushing data * after we unlock adder->collect. * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after * forwarding the seek upstream or from gst_adder_collected, * whichever happens first. */ g_atomic_int_set (&adder->flush_stop_pending, TRUE); } GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event); /* now wait for the collected to be finished and mark a new * segment. After we have the lock, no collect function is running and no * new collect function will be called for as long as we're flushing. */ GST_COLLECT_PADS2_STREAM_LOCK (adder->collect); if (curtype == GST_SEEK_TYPE_SET) adder->segment_start = cur; else adder->segment_start = 0; if (endtype == GST_SEEK_TYPE_SET) adder->segment_end = end; else adder->segment_end = GST_CLOCK_TIME_NONE; if (flush) { /* Yes, we need to call _set_flushing again *WHEN* the streaming threads * have stopped so that the cookie gets properly updated. */ gst_collect_pads2_set_flushing (adder->collect, TRUE); } GST_COLLECT_PADS2_STREAM_UNLOCK (adder->collect); GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT, event); /* we're forwarding seek to all upstream peers and wait for one to reply * with a newsegment-event before we send a newsegment-event downstream */ g_atomic_int_set (&adder->wait_for_new_segment, TRUE); result = forward_event (adder, event, flush); if (!result) { /* seek failed. maybe source is a live source. */ GST_DEBUG_OBJECT (adder, "seeking failed"); } if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending, TRUE, FALSE)) { GST_DEBUG_OBJECT (adder, "pending flush stop"); gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop ()); } break; } case GST_EVENT_QOS: /* QoS might be tricky */ result = FALSE; break; case GST_EVENT_NAVIGATION: /* navigation is rather pointless. */ result = FALSE; break; default: /* just forward the rest for now */ GST_DEBUG_OBJECT (adder, "forward unhandled event: %s", GST_EVENT_TYPE_NAME (event)); result = forward_event (adder, event, FALSE); break; } done: gst_object_unref (adder); return result; }
/* the first caps we receive on any of the sinkpads will define the caps for all * the other sinkpads because we can only mix streams with the same caps. */ static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps) { GstAdder *adder; GList *pads; GstStructure *structure; const char *media_type; adder = GST_ADDER (GST_PAD_PARENT (pad)); GST_LOG_OBJECT (adder, "setting caps on pad %p,%s to %" GST_PTR_FORMAT, pad, GST_PAD_NAME (pad), caps); /* FIXME, see if the other pads can accept the format. Also lock the * format on the other pads to this new format. */ GST_OBJECT_LOCK (adder); pads = GST_ELEMENT (adder)->pads; while (pads) { GstPad *otherpad = GST_PAD (pads->data); if (otherpad != pad) { gst_caps_replace (&GST_PAD_CAPS (otherpad), caps); } pads = g_list_next (pads); } GST_OBJECT_UNLOCK (adder); /* parse caps now */ structure = gst_caps_get_structure (caps, 0); media_type = gst_structure_get_name (structure); if (strcmp (media_type, "audio/x-raw-int") == 0) { adder->format = GST_ADDER_FORMAT_INT; gst_structure_get_int (structure, "width", &adder->width); gst_structure_get_int (structure, "depth", &adder->depth); gst_structure_get_int (structure, "endianness", &adder->endianness); gst_structure_get_boolean (structure, "signed", &adder->is_signed); GST_INFO_OBJECT (pad, "parse_caps sets adder to format int, %d bit", adder->width); if (adder->endianness != G_BYTE_ORDER) goto not_supported; switch (adder->width) { case 8: adder->func = (adder->is_signed ? (GstAdderFunction) add_int8 : (GstAdderFunction) add_uint8); adder->sample_size = 1; break; case 16: adder->func = (adder->is_signed ? (GstAdderFunction) add_int16 : (GstAdderFunction) add_uint16); adder->sample_size = 2; break; case 32: adder->func = (adder->is_signed ? (GstAdderFunction) add_int32 : (GstAdderFunction) add_uint32); adder->sample_size = 4; break; default: goto not_supported; } } else if (strcmp (media_type, "audio/x-raw-float") == 0) { adder->format = GST_ADDER_FORMAT_FLOAT; gst_structure_get_int (structure, "width", &adder->width); gst_structure_get_int (structure, "endianness", &adder->endianness); GST_INFO_OBJECT (pad, "parse_caps sets adder to format float, %d bit", adder->width); if (adder->endianness != G_BYTE_ORDER) goto not_supported; switch (adder->width) { case 32: adder->func = (GstAdderFunction) add_float32; adder->sample_size = 4; break; case 64: adder->func = (GstAdderFunction) add_float64; adder->sample_size = 8; break; default: goto not_supported; } } else { goto not_supported; } gst_structure_get_int (structure, "channels", &adder->channels); gst_structure_get_int (structure, "rate", &adder->rate); /* precalc bps */ adder->bps = (adder->width / 8) * adder->channels; return TRUE; /* ERRORS */ not_supported: { GST_DEBUG_OBJECT (adder, "unsupported format set as caps"); return FALSE; } }
static gboolean gst_adder_sink_event (GstCollectPads * pads, GstCollectData * pad, GstEvent * event, gpointer user_data) { GstAdder *adder = GST_ADDER (user_data); gboolean res = TRUE, discard = FALSE; GST_DEBUG_OBJECT (pad->pad, "Got %s event on sink pad", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); res = gst_adder_setcaps (adder, pad->pad, caps); gst_event_unref (event); event = NULL; break; } case GST_EVENT_FLUSH_START: /* ensure that we will send a flush stop */ res = gst_collect_pads_event_default (pads, pad, event, discard); event = NULL; GST_COLLECT_PADS_STREAM_LOCK (adder->collect); adder->flush_stop_pending = TRUE; GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect); break; case GST_EVENT_FLUSH_STOP: /* we received a flush-stop. We will only forward it when * flush_stop_pending is set, and we will unset it then. */ g_atomic_int_set (&adder->new_segment_pending, TRUE); GST_COLLECT_PADS_STREAM_LOCK (adder->collect); if (adder->flush_stop_pending) { GST_DEBUG_OBJECT (pad->pad, "forwarding flush stop"); res = gst_collect_pads_event_default (pads, pad, event, discard); adder->flush_stop_pending = FALSE; event = NULL; } else { discard = TRUE; GST_DEBUG_OBJECT (pad->pad, "eating flush stop"); } GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect); /* Clear pending tags */ if (adder->pending_events) { g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL); g_list_free (adder->pending_events); adder->pending_events = NULL; } break; case GST_EVENT_TAG: /* collect tags here so we can push them out when we collect data */ adder->pending_events = g_list_append (adder->pending_events, event); event = NULL; break; case GST_EVENT_SEGMENT:{ const GstSegment *segment; gst_event_parse_segment (event, &segment); if (segment->rate != adder->segment.rate) { GST_ERROR_OBJECT (pad->pad, "Got segment event with wrong rate %lf, expected %lf", segment->rate, adder->segment.rate); res = FALSE; gst_event_unref (event); event = NULL; } discard = TRUE; break; } default: break; } if (G_LIKELY (event)) return gst_collect_pads_event_default (pads, pad, event, discard); else return res; }
/* we can only accept caps that we and downstream can handle. * if we have filtercaps set, use those to constrain the target caps. */ static GstCaps * gst_adder_sink_getcaps (GstPad * pad, GstCaps * filter) { GstAdder *adder; GstCaps *result, *peercaps, *current_caps, *filter_caps; GstStructure *s; gint i, n; adder = GST_ADDER (GST_PAD_PARENT (pad)); GST_OBJECT_LOCK (adder); /* take filter */ if ((filter_caps = adder->filter_caps)) { if (filter) filter_caps = gst_caps_intersect_full (filter, filter_caps, GST_CAPS_INTERSECT_FIRST); else gst_caps_ref (filter_caps); } else { filter_caps = filter ? gst_caps_ref (filter) : NULL; } GST_OBJECT_UNLOCK (adder); if (filter_caps && gst_caps_is_empty (filter_caps)) { GST_WARNING_OBJECT (pad, "Empty filter caps"); return filter_caps; } /* get the downstream possible caps */ peercaps = gst_pad_peer_query_caps (adder->srcpad, filter_caps); /* get the allowed caps on this sinkpad */ GST_OBJECT_LOCK (adder); current_caps = adder->current_caps ? gst_caps_ref (adder->current_caps) : NULL; if (current_caps == NULL) { current_caps = gst_pad_get_pad_template_caps (pad); if (!current_caps) current_caps = gst_caps_new_any (); } GST_OBJECT_UNLOCK (adder); if (peercaps) { /* if the peer has caps, intersect */ GST_DEBUG_OBJECT (adder, "intersecting peer and our caps"); result = gst_caps_intersect_full (peercaps, current_caps, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (peercaps); gst_caps_unref (current_caps); } else { /* the peer has no caps (or there is no peer), just use the allowed caps * of this sinkpad. */ /* restrict with filter-caps if any */ if (filter_caps) { GST_DEBUG_OBJECT (adder, "no peer caps, using filtered caps"); result = gst_caps_intersect_full (filter_caps, current_caps, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (current_caps); } else { GST_DEBUG_OBJECT (adder, "no peer caps, using our caps"); result = current_caps; } } result = gst_caps_make_writable (result); n = gst_caps_get_size (result); for (i = 0; i < n; i++) { GstStructure *sref; s = gst_caps_get_structure (result, i); sref = gst_structure_copy (s); gst_structure_set (sref, "channels", GST_TYPE_INT_RANGE, 0, 2, NULL); if (gst_structure_is_subset (s, sref)) { /* This field is irrelevant when in mono or stereo */ gst_structure_remove_field (s, "channel-mask"); } gst_structure_free (sref); } if (filter_caps) gst_caps_unref (filter_caps); GST_LOG_OBJECT (adder, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, pad, GST_PAD_NAME (pad), result); return result; }