static void gst_cutter_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstCutter *filter; g_return_if_fail (GST_IS_CUTTER (object)); filter = GST_CUTTER (object); switch (prop_id) { case PROP_RUN_LENGTH: g_value_set_uint64 (value, filter->threshold_length); break; case PROP_THRESHOLD: g_value_set_double (value, filter->threshold_level); break; case PROP_THRESHOLD_DB: g_value_set_double (value, 20 * log (filter->threshold_level)); break; case PROP_PRE_LENGTH: g_value_set_uint64 (value, filter->pre_length); break; case PROP_LEAKY: g_value_set_boolean (value, filter->leaky); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void gst_cutter_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstCutter *filter; g_return_if_fail (GST_IS_CUTTER (object)); filter = GST_CUTTER (object); switch (prop_id) { case PROP_THRESHOLD: filter->threshold_level = g_value_get_double (value); GST_DEBUG ("DEBUG: set threshold level to %f", filter->threshold_level); break; case PROP_THRESHOLD_DB: /* set the level given in dB * value in dB = 20 * log (value) * values in dB < 0 result in values between 0 and 1 */ filter->threshold_level = pow (10, g_value_get_double (value) / 20); GST_DEBUG_OBJECT (filter, "set threshold level to %f", filter->threshold_level); break; case PROP_RUN_LENGTH: /* set the minimum length of the silent run required */ filter->threshold_length = gst_guint64_to_gdouble (g_value_get_uint64 (value)); break; case PROP_PRE_LENGTH: /* set the length of the pre-record block */ filter->pre_length = gst_guint64_to_gdouble (g_value_get_uint64 (value)); break; case PROP_LEAKY: /* set if the pre-record buffer is leaky or not */ filter->leaky = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static GstFlowReturn gst_cutter_chain (GstPad * pad, GstBuffer * buf) { GstCutter *filter; gint16 *in_data; guint num_samples; gdouble NCS = 0.0; /* Normalized Cumulative Square of buffer */ gdouble RMS = 0.0; /* RMS of signal in buffer */ gdouble NMS = 0.0; /* Normalized Mean Square of buffer */ GstBuffer *prebuf; /* pointer to a prebuffer element */ g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR); g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR); g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); filter = GST_CUTTER (GST_OBJECT_PARENT (pad)); g_return_val_if_fail (filter != NULL, GST_FLOW_ERROR); g_return_val_if_fail (GST_IS_CUTTER (filter), GST_FLOW_ERROR); if (!filter->have_caps) { if (!(gst_cutter_get_caps (pad, filter))) return GST_FLOW_NOT_NEGOTIATED; } in_data = (gint16 *) GST_BUFFER_DATA (buf); GST_LOG_OBJECT (filter, "length of prerec buffer: %" GST_TIME_FORMAT, GST_TIME_ARGS (filter->pre_run_length)); /* calculate mean square value on buffer */ switch (filter->width) { case 16: num_samples = GST_BUFFER_SIZE (buf) / 2; gst_cutter_calculate_gint16 (in_data, num_samples, &NCS); NMS = NCS / num_samples; break; case 8: num_samples = GST_BUFFER_SIZE (buf); gst_cutter_calculate_gint8 ((gint8 *) in_data, num_samples, &NCS); NMS = NCS / num_samples; break; default: /* this shouldn't happen */ g_warning ("no mean square function for width %d\n", filter->width); break; } filter->silent_prev = filter->silent; RMS = sqrt (NMS); /* if RMS below threshold, add buffer length to silent run length count * if not, reset */ GST_LOG_OBJECT (filter, "buffer stats: NMS %f, RMS %f, audio length %f", NMS, RMS, gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer (filter->sinkpad, buf))); if (RMS < filter->threshold_level) filter->silent_run_length += gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer (filter->sinkpad, buf)); else { filter->silent_run_length = 0 * GST_SECOND; filter->silent = FALSE; } if (filter->silent_run_length > filter->threshold_length) /* it has been silent long enough, flag it */ filter->silent = TRUE; /* has the silent status changed ? if so, send right signal * and, if from silent -> not silent, flush pre_record buffer */ if (filter->silent != filter->silent_prev) { if (filter->silent) { GstMessage *m = gst_cutter_message_new (filter, FALSE, GST_BUFFER_TIMESTAMP (buf)); GST_DEBUG_OBJECT (filter, "signaling CUT_STOP"); gst_element_post_message (GST_ELEMENT (filter), m); } else { gint count = 0; GstMessage *m = gst_cutter_message_new (filter, TRUE, GST_BUFFER_TIMESTAMP (buf)); GST_DEBUG_OBJECT (filter, "signaling CUT_START"); gst_element_post_message (GST_ELEMENT (filter), m); /* first of all, flush current buffer */ GST_DEBUG_OBJECT (filter, "flushing buffer of length %" GST_TIME_FORMAT, GST_TIME_ARGS (filter->pre_run_length)); while (filter->pre_buffer) { prebuf = (g_list_first (filter->pre_buffer))->data; filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf); gst_pad_push (filter->srcpad, prebuf); ++count; } GST_DEBUG_OBJECT (filter, "flushed %d buffers", count); filter->pre_run_length = 0 * GST_SECOND; } } /* now check if we have to send the new buffer to the internal buffer cache * or to the srcpad */ if (filter->silent) { filter->pre_buffer = g_list_append (filter->pre_buffer, buf); filter->pre_run_length += gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer (filter->sinkpad, buf)); while (filter->pre_run_length > filter->pre_length) { prebuf = (g_list_first (filter->pre_buffer))->data; g_assert (GST_IS_BUFFER (prebuf)); filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf); filter->pre_run_length -= gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer (filter->sinkpad, prebuf)); /* only pass buffers if we don't leak */ if (!filter->leaky) gst_pad_push (filter->srcpad, prebuf); else gst_buffer_unref (prebuf); } } else gst_pad_push (filter->srcpad, buf); return GST_FLOW_OK; }