/* see if pads were added or removed and update our stats. Any pad * added after releasing the PAD_LOCK will get collected in the next * round. * * We can do a quick check by checking the cookies, that get changed * whenever the pad list is updated. * * Must be called with LOCK. */ static void gst_collect_pads_check_pads_unlocked (GstCollectPads * pads) { GST_DEBUG ("stored cookie : %d, used_cookie:%d", pads->abidata.ABI.pad_cookie, pads->cookie); if (G_UNLIKELY (pads->abidata.ABI.pad_cookie != pads->cookie)) { GSList *collected; /* clear list and stats */ g_slist_foreach (pads->data, (GFunc) unref_data, NULL); g_slist_free (pads->data); pads->data = NULL; pads->numpads = 0; pads->queuedpads = 0; pads->eospads = 0; /* loop over the master pad list */ collected = pads->abidata.ABI.pad_list; for (; collected; collected = g_slist_next (collected)) { GstCollectData *data; /* update the stats */ pads->numpads++; data = collected->data; if (G_LIKELY (!data->abidata.ABI.flushing)) { if (data->buffer) pads->queuedpads++; if (data->abidata.ABI.eos) pads->eospads++; } /* add to the list of pads to collect */ ref_data (data); pads->data = g_slist_prepend (pads->data, data); } /* and update the cookie */ pads->cookie = pads->abidata.ABI.pad_cookie; } }
void MultiChannelCartesianGrappaReconGadget::make_ref_coil_map(IsmrmrdDataBuffered& ref_, std::vector<size_t> recon_dims, ReconObjType& recon_obj, size_t encoding) { try { hoNDArray< std::complex<float> >& ref_data = ref_.data_; hoNDArray< std::complex<float> >& ref_calib = recon_obj.ref_calib_; hoNDArray< std::complex<float> >& ref_coil_map = recon_obj.ref_coil_map_; // sampling limits size_t sRO = ref_.sampling_.sampling_limits_[0].min_; size_t eRO = ref_.sampling_.sampling_limits_[0].max_; size_t cRO = ref_.sampling_.sampling_limits_[0].center_; size_t sE1 = ref_.sampling_.sampling_limits_[1].min_; size_t eE1 = ref_.sampling_.sampling_limits_[1].max_; size_t cE1 = ref_.sampling_.sampling_limits_[1].center_; size_t sE2 = ref_.sampling_.sampling_limits_[2].min_; size_t eE2 = ref_.sampling_.sampling_limits_[2].max_; size_t cE2 = ref_.sampling_.sampling_limits_[2].center_; // recon size size_t recon_RO = recon_dims[0]; size_t recon_E1 = recon_dims[1]; size_t recon_E2 = recon_dims[2]; // ref array size size_t CHA = ref_data.get_size(3); size_t N = ref_data.get_size(4); size_t S = ref_data.get_size(5); size_t SLC = ref_data.get_size(6); // determine the ref_coil_map size size_t RO = 2 * cRO; if (sRO>0 || eRO<RO - 1) { RO = 2 * std::max(cRO - sRO, eRO - cRO+1); if (RO>recon_RO) RO = recon_RO; } size_t E1 = eE1 - sE1 + 1; size_t E2 = eE2 - sE2 + 1; if ((calib_mode_[encoding] == Gadgetron::ISMRMRD_interleaved) || (calib_mode_[encoding] == Gadgetron::ISMRMRD_noacceleration)) { E1 = 2 * std::max(cE1 - sE1, eE1 - cE1+1); if (E1>recon_E1) E1 = recon_E1; if (E2 > 1) { E2 = 2 * std::max(cE2 - sE2, eE2 - cE2 + 1); if (E2 > recon_E2) E2 = recon_E2; } } ref_coil_map.create(RO, E1, E2, CHA, N, S, SLC); Gadgetron::clear(ref_coil_map); size_t slc, s, n, cha, e2, e1; for (slc = 0; slc < SLC; slc++) { for (s = 0; s < S; s++) { for (n = 0; n < N; n++) { for (cha = 0; cha < CHA; cha++) { for (e2 = sE2; e2 <= eE2; e2++) { for (e1 = sE1; e1 <= eE1; e1++) { std::complex<float>* pSrc = &(ref_data(0, e1-sE1, e2-sE2, cha, n, s, slc)); std::complex<float>* pDst = &(ref_coil_map(0, e1, e2, cha, n, s, slc)); memcpy(pDst + sRO, pSrc, sizeof(std::complex<float>)*(eRO - sRO + 1)); } } } } } } // filter the ref_coil_map if (filter_RO_ref_coi_map_.get_size(0) != RO) { Gadgetron::generate_symmetric_filter_ref(ref_coil_map.get_size(0), ref_.sampling_.sampling_limits_[0].min_, ref_.sampling_.sampling_limits_[0].max_, filter_RO_ref_coi_map_); } if (filter_E1_ref_coi_map_.get_size(0) != E1) { Gadgetron::generate_symmetric_filter_ref(ref_coil_map.get_size(1), ref_.sampling_.sampling_limits_[1].min_, ref_.sampling_.sampling_limits_[1].max_, filter_E1_ref_coi_map_); } if ( (E2 > 1) && (filter_E2_ref_coi_map_.get_size(0) != E2) ) { Gadgetron::generate_symmetric_filter_ref(ref_coil_map.get_size(2), ref_.sampling_.sampling_limits_[2].min_, ref_.sampling_.sampling_limits_[2].max_, filter_E2_ref_coi_map_); } hoNDArray< std::complex<float> > ref_recon_buf; if (E2 > 1) { Gadgetron::apply_kspace_filter_ROE1E2(ref_coil_map, filter_RO_ref_coi_map_, filter_E1_ref_coi_map_, filter_E2_ref_coi_map_, ref_recon_buf); } else { Gadgetron::apply_kspace_filter_ROE1(ref_coil_map, filter_RO_ref_coi_map_, filter_E1_ref_coi_map_, ref_recon_buf); } // pad the ref_coil_map into the data array Gadgetron::pad(recon_RO, recon_E1, recon_E2, &ref_recon_buf, &ref_coil_map); std::vector<size_t> dim = *ref_data.get_dimensions(); ref_calib.create(dim, ref_data.begin()); } catch (...) { GADGET_THROW("Errors happened in MultiChannelCartesianGrappaReconGadget::make_ref_coil_map(...) ... "); } }
/* For each buffer we receive we check if our collected condition is reached * and if so we call the collected function. When this is done we check if * data has been unqueued. If data is still queued we wait holding the stream * lock to make sure no EOS event can happen while we are ready to be * collected */ static GstFlowReturn gst_collect_pads_chain (GstPad * pad, GstBuffer * buffer) { GstCollectData *data; GstCollectPads *pads; GstCollectPadsPrivate *priv; GstFlowReturn ret; GST_DEBUG ("Got buffer for pad %s:%s", GST_DEBUG_PAD_NAME (pad)); /* some magic to get the managing collect_pads */ GST_OBJECT_LOCK (pad); data = (GstCollectData *) gst_pad_get_element_private (pad); if (G_UNLIKELY (data == NULL)) goto no_data; ref_data (data); GST_OBJECT_UNLOCK (pad); pads = data->collect; priv = pads->abidata.ABI.priv; GST_OBJECT_LOCK (pads); /* if not started, bail out */ if (G_UNLIKELY (!pads->started)) goto not_started; /* check if this pad is flushing */ if (G_UNLIKELY (data->abidata.ABI.flushing)) goto flushing; /* pad was EOS, we can refuse this data */ if (G_UNLIKELY (data->abidata.ABI.eos)) goto unexpected; /* see if we need to clip */ if (priv->clipfunc) { buffer = priv->clipfunc (pads, data, buffer, priv->clipfunc_user_data); if (G_UNLIKELY (buffer == NULL)) goto clipped; } GST_DEBUG ("Queuing buffer %p for pad %s:%s", buffer, GST_DEBUG_PAD_NAME (pad)); /* One more pad has data queued */ pads->queuedpads++; /* take ownership of the buffer */ if (data->buffer) gst_buffer_unref (data->buffer); data->buffer = buffer; buffer = NULL; /* update segment last position if in TIME */ if (G_LIKELY (data->segment.format == GST_FORMAT_TIME)) { GstClockTime timestamp = GST_BUFFER_TIMESTAMP (data->buffer); if (GST_CLOCK_TIME_IS_VALID (timestamp)) gst_segment_set_last_stop (&data->segment, GST_FORMAT_TIME, timestamp); } /* While we have data queued on this pad try to collect stuff */ do { GST_DEBUG ("Pad %s:%s checking", GST_DEBUG_PAD_NAME (pad)); /* Check if our collected condition is matched and call the collected function * if it is */ ret = gst_collect_pads_check_collected (pads); /* when an error occurs, we want to report this back to the caller ASAP * without having to block if the buffer was not popped */ if (G_UNLIKELY (ret != GST_FLOW_OK)) goto error; /* data was consumed, we can exit and accept new data */ if (data->buffer == NULL) break; /* Check if we got removed in the mean time, FIXME, this is racy. * Between this check and the _WAIT, the pad could be removed which will * makes us hang in the _WAIT. */ GST_OBJECT_LOCK (pad); if (G_UNLIKELY (gst_pad_get_element_private (pad) == NULL)) goto pad_removed; GST_OBJECT_UNLOCK (pad); GST_DEBUG ("Pad %s:%s has a buffer queued, waiting", GST_DEBUG_PAD_NAME (pad)); /* wait to be collected, this must happen from another thread triggered * by the _chain function of another pad. We release the lock so we * can get stopped or flushed as well. We can however not get EOS * because we still hold the STREAM_LOCK. */ GST_COLLECT_PADS_WAIT (pads); GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad)); /* after a signal, we could be stopped */ if (G_UNLIKELY (!pads->started)) goto not_started; /* check if this pad is flushing */ if (G_UNLIKELY (data->abidata.ABI.flushing)) goto flushing; } while (data->buffer != NULL); unlock_done: GST_DEBUG ("Pad %s:%s done", GST_DEBUG_PAD_NAME (pad)); GST_OBJECT_UNLOCK (pads); unref_data (data); if (buffer) gst_buffer_unref (buffer); return ret; pad_removed: { GST_WARNING ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); GST_OBJECT_UNLOCK (pad); ret = GST_FLOW_NOT_LINKED; goto unlock_done; } /* ERRORS */ no_data: { GST_DEBUG ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); GST_OBJECT_UNLOCK (pad); gst_buffer_unref (buffer); return GST_FLOW_NOT_LINKED; } not_started: { GST_DEBUG ("not started"); gst_collect_pads_clear (pads, data); ret = GST_FLOW_WRONG_STATE; goto unlock_done; } flushing: { GST_DEBUG ("pad %s:%s is flushing", GST_DEBUG_PAD_NAME (pad)); gst_collect_pads_clear (pads, data); ret = GST_FLOW_WRONG_STATE; goto unlock_done; } unexpected: { /* we should not post an error for this, just inform upstream that * we don't expect anything anymore */ GST_DEBUG ("pad %s:%s is eos", GST_DEBUG_PAD_NAME (pad)); ret = GST_FLOW_UNEXPECTED; goto unlock_done; } clipped: { GST_DEBUG ("clipped buffer on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); ret = GST_FLOW_OK; goto unlock_done; } error: { /* we print the error, the element should post a reasonable error * message for fatal errors */ GST_DEBUG ("collect failed, reason %d (%s)", ret, gst_flow_get_name (ret)); gst_collect_pads_clear (pads, data); goto unlock_done; } }
static gboolean gst_collect_pads_event (GstPad * pad, GstEvent * event) { gboolean res; GstCollectData *data; GstCollectPads *pads; /* some magic to get the managing collect_pads */ GST_OBJECT_LOCK (pad); data = (GstCollectData *) gst_pad_get_element_private (pad); if (G_UNLIKELY (data == NULL)) goto pad_removed; ref_data (data); GST_OBJECT_UNLOCK (pad); res = TRUE; pads = data->collect; GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (data->pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: { /* forward event to unblock check_collected */ gst_pad_event_default (pad, event); /* now unblock the chain function. * no cond per pad, so they all unblock, * non-flushing block again */ GST_OBJECT_LOCK (pads); data->abidata.ABI.flushing = TRUE; gst_collect_pads_clear (pads, data); GST_OBJECT_UNLOCK (pads); /* event already cleaned up by forwarding */ goto done; } case GST_EVENT_FLUSH_STOP: { /* flush the 1 buffer queue */ GST_OBJECT_LOCK (pads); data->abidata.ABI.flushing = FALSE; gst_collect_pads_clear (pads, data); /* we need new segment info after the flush */ gst_segment_init (&data->segment, GST_FORMAT_UNDEFINED); data->abidata.ABI.new_segment = FALSE; /* if the pad was EOS, remove the EOS flag and * decrement the number of eospads */ if (G_UNLIKELY (data->abidata.ABI.eos == TRUE)) { pads->eospads--; data->abidata.ABI.eos = FALSE; } if (!gst_collect_pads_is_flushing (pads)) { /* forward event if all pads are no longer flushing */ GST_DEBUG ("No more pads are flushing, forwarding FLUSH_STOP"); GST_OBJECT_UNLOCK (pads); goto forward; } gst_event_unref (event); GST_OBJECT_UNLOCK (pads); goto done; } case GST_EVENT_EOS: { GST_OBJECT_LOCK (pads); /* if the pad was not EOS, make it EOS and so we * have one more eospad */ if (G_LIKELY (data->abidata.ABI.eos == FALSE)) { data->abidata.ABI.eos = TRUE; pads->eospads++; } /* check if we need collecting anything, we ignore the * result. */ gst_collect_pads_check_collected (pads); GST_OBJECT_UNLOCK (pads); /* We eat this event, element should do something * in the collected callback. */ gst_event_unref (event); goto done; } case GST_EVENT_NEWSEGMENT: { gint64 start, stop, time; gdouble rate, arate; GstFormat format; gboolean update; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); GST_DEBUG_OBJECT (data->pad, "got newsegment, start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); gst_segment_set_newsegment_full (&data->segment, update, rate, arate, format, start, stop, time); data->abidata.ABI.new_segment = TRUE; /* we must not forward this event since multiple segments will be * accumulated and this is certainly not what we want. */ gst_event_unref (event); /* FIXME: collect-pads based elements need to create their own newsegment * event (and only one really) * (a) make the segment part of the GstCollectData structure of each pad, * so you can just check that once you have a buffer queued on that pad, * (b) you can override a pad's event function with your own, * catch the newsegment event and then pass it on to the original * gstcollectpads event function * (that's what avimux does for something IIRC) * see #340060 */ goto done; } default: /* forward other events */ goto forward; } forward: GST_DEBUG_OBJECT (pads, "forward unhandled event: %s", GST_EVENT_TYPE_NAME (event)); res = gst_pad_event_default (pad, event); done: unref_data (data); return res; /* ERRORS */ pad_removed: { GST_DEBUG ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); GST_OBJECT_UNLOCK (pad); return FALSE; } }