/* GstBaseTransform vmethod implementations */ static GstFlowReturn gst_audio_invert_transform_ip (GstBaseTransform * base, GstBuffer * buf) { GstAudioInvert *filter = GST_AUDIO_INVERT (base); guint num_samples; GstClockTime timestamp, stream_time; GstMapInfo map; timestamp = GST_BUFFER_TIMESTAMP (buf); stream_time = gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp); GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); if (GST_CLOCK_TIME_IS_VALID (stream_time)) gst_object_sync_values (GST_OBJECT (filter), stream_time); if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP))) return GST_FLOW_OK; gst_buffer_map (buf, &map, GST_MAP_READWRITE); num_samples = map.size / GST_AUDIO_FILTER_BPS (filter); filter->process (filter, map.data, num_samples); gst_buffer_unmap (buf, &map); return GST_FLOW_OK; }
/* GstBaseTransform vmethod implementations */ static GstFlowReturn gst_audio_echo_transform_ip (GstBaseTransform * base, GstBuffer * buf) { GstAudioEcho *self = GST_AUDIO_ECHO (base); guint num_samples; GstClockTime timestamp, stream_time; GstMapInfo map; g_mutex_lock (&self->lock); timestamp = GST_BUFFER_TIMESTAMP (buf); stream_time = gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp); GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); if (GST_CLOCK_TIME_IS_VALID (stream_time)) gst_object_sync_values (GST_OBJECT (self), stream_time); if (self->buffer == NULL) { guint bpf, rate; bpf = GST_AUDIO_FILTER_BPF (self); rate = GST_AUDIO_FILTER_RATE (self); self->delay_frames = MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1); self->buffer_size_frames = MAX (gst_util_uint64_scale (self->max_delay, rate, GST_SECOND), 1); self->buffer_size = self->buffer_size_frames * bpf; self->buffer = g_try_malloc0 (self->buffer_size); self->buffer_pos = 0; if (self->buffer == NULL) { g_mutex_unlock (&self->lock); GST_ERROR_OBJECT (self, "Failed to allocate %u bytes", self->buffer_size); return GST_FLOW_ERROR; } } gst_buffer_map (buf, &map, GST_MAP_READWRITE); num_samples = map.size / GST_AUDIO_FILTER_BPS (self); self->process (self, map.data, num_samples); gst_buffer_unmap (buf, &map); g_mutex_unlock (&self->lock); return GST_FLOW_OK; }
/* GstBaseTransform vmethod implementations */ static GstFlowReturn gst_audio_fx_base_iir_filter_transform_ip (GstBaseTransform * base, GstBuffer * buf) { GstAudioFXBaseIIRFilter *filter = GST_AUDIO_FX_BASE_IIR_FILTER (base); guint num_samples; GstClockTime timestamp, stream_time; GstMapInfo map; timestamp = GST_BUFFER_TIMESTAMP (buf); stream_time = gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp); GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); if (GST_CLOCK_TIME_IS_VALID (stream_time)) gst_object_sync_values (GST_OBJECT (filter), stream_time); gst_buffer_map (buf, &map, GST_MAP_READWRITE); num_samples = map.size / GST_AUDIO_FILTER_BPS (filter); g_mutex_lock (&filter->lock); if (filter->a == NULL || filter->b == NULL) { g_warn_if_fail (filter->a != NULL && filter->b != NULL); gst_buffer_unmap (buf, &map); g_mutex_unlock (&filter->lock); return GST_FLOW_ERROR; } filter->process (filter, map.data, num_samples); g_mutex_unlock (&filter->lock); gst_buffer_unmap (buf, &map); return GST_FLOW_OK; }
static GstFlowReturn gst_audio_fx_base_fir_filter_transform (GstBaseTransform * base, GstBuffer * inbuf, GstBuffer * outbuf) { GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (base); GstClockTime timestamp, expected_timestamp; gint channels = GST_AUDIO_FILTER_CHANNELS (self); gint rate = GST_AUDIO_FILTER_RATE (self); gint bps = GST_AUDIO_FILTER_BPS (self); GstMapInfo inmap, outmap; guint input_samples; guint output_samples; guint generated_samples; guint64 output_offset; gint64 diff = 0; GstClockTime stream_time; timestamp = GST_BUFFER_TIMESTAMP (outbuf); if (!GST_CLOCK_TIME_IS_VALID (timestamp) && !GST_CLOCK_TIME_IS_VALID (self->start_ts)) { GST_ERROR_OBJECT (self, "Invalid timestamp"); return GST_FLOW_ERROR; } g_mutex_lock (&self->lock); stream_time = gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp); GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); if (GST_CLOCK_TIME_IS_VALID (stream_time)) gst_object_sync_values (GST_OBJECT (self), stream_time); g_return_val_if_fail (self->kernel != NULL, GST_FLOW_ERROR); g_return_val_if_fail (channels != 0, GST_FLOW_ERROR); if (GST_CLOCK_TIME_IS_VALID (self->start_ts)) expected_timestamp = self->start_ts + gst_util_uint64_scale_int (self->nsamples_in, GST_SECOND, rate); else expected_timestamp = GST_CLOCK_TIME_NONE; /* Reset the residue if already existing on discont buffers */ if (GST_BUFFER_IS_DISCONT (inbuf) || (GST_CLOCK_TIME_IS_VALID (expected_timestamp) && (ABS (GST_CLOCK_DIFF (timestamp, expected_timestamp) > 5 * GST_MSECOND)))) { GST_DEBUG_OBJECT (self, "Discontinuity detected - flushing"); if (GST_CLOCK_TIME_IS_VALID (expected_timestamp)) gst_audio_fx_base_fir_filter_push_residue (self); self->buffer_fill = 0; g_free (self->buffer); self->buffer = NULL; self->start_ts = timestamp; self->start_off = GST_BUFFER_OFFSET (inbuf); self->nsamples_out = 0; self->nsamples_in = 0; } else if (!GST_CLOCK_TIME_IS_VALID (self->start_ts)) { self->start_ts = timestamp; self->start_off = GST_BUFFER_OFFSET (inbuf); } gst_buffer_map (inbuf, &inmap, GST_MAP_READ); gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); input_samples = (inmap.size / bps) / channels; output_samples = (outmap.size / bps) / channels; self->nsamples_in += input_samples; generated_samples = self->process (self, inmap.data, outmap.data, input_samples); gst_buffer_unmap (inbuf, &inmap); gst_buffer_unmap (outbuf, &outmap); g_assert (generated_samples <= output_samples); self->nsamples_out += generated_samples; if (generated_samples == 0) goto no_samples; /* Calculate the number of samples we can push out now without outputting * latency zeros in the beginning */ diff = ((gint64) self->nsamples_out) - ((gint64) self->latency); if (diff < 0) goto no_samples; if (diff < generated_samples) { gint64 tmp = diff; diff = generated_samples - diff; generated_samples = tmp; } else { diff = 0; } gst_buffer_resize (outbuf, diff * bps * channels, generated_samples * bps * channels); output_offset = self->nsamples_out - self->latency - generated_samples; GST_BUFFER_TIMESTAMP (outbuf) = self->start_ts + gst_util_uint64_scale_int (output_offset, GST_SECOND, rate); GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int (output_samples, GST_SECOND, rate); if (self->start_off != GST_BUFFER_OFFSET_NONE) { GST_BUFFER_OFFSET (outbuf) = self->start_off + output_offset; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + generated_samples; } else { GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE; } g_mutex_unlock (&self->lock); GST_DEBUG_OBJECT (self, "Pushing buffer of size %" G_GSIZE_FORMAT " with timestamp: %" GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %" G_GUINT64_FORMAT ", offset_end: %" G_GUINT64_FORMAT ", nsamples_out: %d", gst_buffer_get_size (outbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf), generated_samples); return GST_FLOW_OK; no_samples: { g_mutex_unlock (&self->lock); return GST_BASE_TRANSFORM_FLOW_DROPPED; } }
void gst_audio_fx_base_fir_filter_push_residue (GstAudioFXBaseFIRFilter * self) { GstBuffer *outbuf; GstFlowReturn res; gint rate = GST_AUDIO_FILTER_RATE (self); gint channels = GST_AUDIO_FILTER_CHANNELS (self); gint bps = GST_AUDIO_FILTER_BPS (self); gint outsize, outsamples; GstMapInfo map; guint8 *in, *out; if (channels == 0 || rate == 0 || self->nsamples_in == 0) { self->buffer_fill = 0; g_free (self->buffer); self->buffer = NULL; return; } /* Calculate the number of samples and their memory size that * should be pushed from the residue */ outsamples = self->nsamples_in - (self->nsamples_out - self->latency); if (outsamples <= 0) { self->buffer_fill = 0; g_free (self->buffer); self->buffer = NULL; return; } outsize = outsamples * channels * bps; if (!self->fft || self->low_latency) { gint64 diffsize, diffsamples; /* Process the difference between latency and residue length samples * to start at the actual data instead of starting at the zeros before * when we only got one buffer smaller than latency */ diffsamples = ((gint64) self->latency) - ((gint64) self->buffer_fill) / channels; if (diffsamples > 0) { diffsize = diffsamples * channels * bps; in = g_new0 (guint8, diffsize); out = g_new0 (guint8, diffsize); self->nsamples_out += self->process (self, in, out, diffsamples); g_free (in); g_free (out); } outbuf = gst_buffer_new_and_alloc (outsize); /* Convolve the residue with zeros to get the actual remaining data */ in = g_new0 (guint8, outsize); gst_buffer_map (outbuf, &map, GST_MAP_READWRITE); self->nsamples_out += self->process (self, in, map.data, outsamples); gst_buffer_unmap (outbuf, &map); g_free (in); } else { guint gensamples = 0; outbuf = gst_buffer_new_and_alloc (outsize); gst_buffer_map (outbuf, &map, GST_MAP_READWRITE); while (gensamples < outsamples) { guint step_insamples = self->block_length - self->buffer_fill; guint8 *zeroes = g_new0 (guint8, step_insamples * channels * bps); guint8 *out = g_new (guint8, self->block_length * channels * bps); guint step_gensamples; step_gensamples = self->process (self, zeroes, out, step_insamples); g_free (zeroes); memcpy (map.data + gensamples * bps, out, MIN (step_gensamples, outsamples - gensamples) * bps); gensamples += MIN (step_gensamples, outsamples - gensamples); g_free (out); } self->nsamples_out += gensamples; gst_buffer_unmap (outbuf, &map); } /* Set timestamp, offset, etc from the values we * saved when processing the regular buffers */ if (GST_CLOCK_TIME_IS_VALID (self->start_ts)) GST_BUFFER_TIMESTAMP (outbuf) = self->start_ts; else GST_BUFFER_TIMESTAMP (outbuf) = 0; GST_BUFFER_TIMESTAMP (outbuf) += gst_util_uint64_scale_int (self->nsamples_out - outsamples - self->latency, GST_SECOND, rate); GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int (outsamples, GST_SECOND, rate); if (self->start_off != GST_BUFFER_OFFSET_NONE) { GST_BUFFER_OFFSET (outbuf) = self->start_off + self->nsamples_out - outsamples - self->latency; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + outsamples; } GST_DEBUG_OBJECT (self, "Pushing residue buffer of size %" G_GSIZE_FORMAT " with timestamp: %" GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %" G_GUINT64_FORMAT ", offset_end: %" G_GUINT64_FORMAT ", nsamples_out: %d", gst_buffer_get_size (outbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf), outsamples); res = gst_pad_push (GST_BASE_TRANSFORM_CAST (self)->srcpad, outbuf); if (G_UNLIKELY (res != GST_FLOW_OK)) { GST_WARNING_OBJECT (self, "failed to push residue"); } self->buffer_fill = 0; }
static GstFlowReturn gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) { GstSpectrum *spectrum = GST_SPECTRUM (trans); guint rate = GST_AUDIO_FILTER_RATE (spectrum); guint channels = GST_AUDIO_FILTER_CHANNELS (spectrum); guint bps = GST_AUDIO_FILTER_BPS (spectrum); guint bpf = GST_AUDIO_FILTER_BPF (spectrum); guint output_channels = spectrum->multi_channel ? channels : 1; guint c; gfloat max_value = (1UL << ((bps << 3) - 1)) - 1; guint bands = spectrum->bands; guint nfft = 2 * bands - 2; guint input_pos; gfloat *input; GstMapInfo map; const guint8 *data; gsize size; guint fft_todo, msg_todo, block_size; gboolean have_full_interval; GstSpectrumChannel *cd; GstSpectrumInputData input_data; g_mutex_lock (&spectrum->lock); gst_buffer_map (buffer, &map, GST_MAP_READ); data = map.data; size = map.size; GST_LOG_OBJECT (spectrum, "input size: %" G_GSIZE_FORMAT " bytes", size); if (GST_BUFFER_IS_DISCONT (buffer)) { GST_DEBUG_OBJECT (spectrum, "Discontinuity detected -- flushing"); gst_spectrum_flush (spectrum); } /* If we don't have a FFT context yet (or it was reset due to parameter * changes) get one and allocate memory for everything */ if (spectrum->channel_data == NULL) { GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands); gst_spectrum_alloc_channel_data (spectrum); /* number of sample frames we process before posting a message * interval is in ns */ spectrum->frames_per_interval = gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND); spectrum->frames_todo = spectrum->frames_per_interval; /* rounding error for frames_per_interval in ns, * aggregated it in accumulated_error */ spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND; if (spectrum->frames_per_interval == 0) spectrum->frames_per_interval = 1; GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %" G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT, GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval, GST_TIME_ARGS (spectrum->error_per_interval)); spectrum->input_pos = 0; gst_spectrum_flush (spectrum); } if (spectrum->num_frames == 0) spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer); input_pos = spectrum->input_pos; input_data = spectrum->input_data; while (size >= bpf) { /* run input_data for a chunk of data */ fft_todo = nfft - (spectrum->num_frames % nfft); msg_todo = spectrum->frames_todo - spectrum->num_frames; GST_LOG_OBJECT (spectrum, "message frames todo: %u, fft frames todo: %u, input frames %" G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf)); block_size = msg_todo; if (block_size > (size / bpf)) block_size = (size / bpf); if (block_size > fft_todo) block_size = fft_todo; for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; input = cd->input; /* Move the current frames into our ringbuffers */ input_data (data + c * bps, input, block_size, channels, max_value, input_pos, nfft); } data += block_size * bpf; size -= block_size * bpf; input_pos = (input_pos + block_size) % nfft; spectrum->num_frames += block_size; have_full_interval = (spectrum->num_frames == spectrum->frames_todo); GST_LOG_OBJECT (spectrum, "size: %" G_GSIZE_FORMAT ", do-fft = %d, do-message = %d", size, (spectrum->num_frames % nfft == 0), have_full_interval); /* If we have enough frames for an FFT or we have all frames required for * the interval and we haven't run a FFT, then run an FFT */ if ((spectrum->num_frames % nfft == 0) || (have_full_interval && !spectrum->num_fft)) { for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; gst_spectrum_run_fft (spectrum, cd, input_pos); } spectrum->num_fft++; } /* Do we have the FFTs for one interval? */ if (have_full_interval) { GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft, spectrum->num_frames, spectrum->frames_per_interval, GST_TIME_ARGS (spectrum->accumulated_error)); spectrum->frames_todo = spectrum->frames_per_interval; if (spectrum->accumulated_error >= GST_SECOND) { spectrum->accumulated_error -= GST_SECOND; spectrum->frames_todo++; } spectrum->accumulated_error += spectrum->error_per_interval; if (spectrum->post_messages) { GstMessage *m; for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; gst_spectrum_prepare_message_data (spectrum, cd); } m = gst_spectrum_message_new (spectrum, spectrum->message_ts, spectrum->interval); gst_element_post_message (GST_ELEMENT (spectrum), m); } if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts)) spectrum->message_ts += gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate); for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; gst_spectrum_reset_message_data (spectrum, cd); } spectrum->num_frames = 0; spectrum->num_fft = 0; } } spectrum->input_pos = input_pos; gst_buffer_unmap (buffer, &map); g_mutex_unlock (&spectrum->lock); g_assert (size == 0); return GST_FLOW_OK; }