cutter: bring cutter somewhat into this millennium

This commit is contained in:
Wim Taymans 2011-08-20 10:46:18 +02:00
parent c1abdd7626
commit 5e3a52f1d9
2 changed files with 83 additions and 68 deletions

View file

@ -100,10 +100,9 @@ static void gst_cutter_set_property (GObject * object, guint prop_id,
static void gst_cutter_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_cutter_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_cutter_chain (GstPad * pad, GstBuffer * buffer);
static gboolean gst_cutter_get_caps (GstPad * pad, GstCutter * filter);
static void
gst_cutter_class_init (GstCutterClass * klass)
{
@ -156,8 +155,15 @@ gst_cutter_init (GstCutter * filter)
{
filter->sinkpad =
gst_pad_new_from_static_template (&cutter_sink_factory, "sink");
gst_pad_set_chain_function (filter->sinkpad, gst_cutter_chain);
gst_pad_set_event_function (filter->sinkpad, gst_cutter_event);
gst_pad_use_fixed_caps (filter->sinkpad);
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
filter->srcpad =
gst_pad_new_from_static_template (&cutter_src_factory, "src");
gst_pad_use_fixed_caps (filter->srcpad);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
filter->threshold_level = CUTTER_DEFAULT_THRESHOLD_LEVEL;
filter->threshold_length = CUTTER_DEFAULT_THRESHOLD_LENGTH;
@ -169,13 +175,6 @@ gst_cutter_init (GstCutter * filter)
filter->pre_run_length = 0 * GST_SECOND;
filter->pre_buffer = NULL;
filter->leaky = FALSE;
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
gst_pad_set_chain_function (filter->sinkpad, gst_cutter_chain);
gst_pad_use_fixed_caps (filter->sinkpad);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
gst_pad_use_fixed_caps (filter->srcpad);
}
static GstMessage *
@ -223,51 +222,86 @@ gst_cutter_calculate_##TYPE (TYPE * in, guint num, \
DEFINE_CUTTER_CALCULATOR (gint16, 15);
DEFINE_CUTTER_CALCULATOR (gint8, 7);
static gboolean
gst_cutter_setcaps (GstCutter * filter, GstCaps * caps)
{
GstAudioInfo info;
if (!gst_audio_info_from_caps (&info, caps))
return FALSE;
filter->info = info;
return gst_pad_set_caps (filter->srcpad, caps);
}
static gboolean
gst_cutter_event (GstPad * pad, GstEvent * event)
{
gboolean ret;
GstCutter *filter;
filter = GST_CUTTER (GST_OBJECT_PARENT (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
ret = gst_cutter_setcaps (filter, caps);
break;
}
default:
ret = gst_pad_event_default (pad, event);
break;
}
return ret;
}
static GstFlowReturn
gst_cutter_chain (GstPad * pad, GstBuffer * buf)
{
GstFlowReturn ret = GST_FLOW_OK;
GstCutter *filter;
gint16 *in_data;
gint bpf, rate;
gsize in_size;
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);
GstClockTime duration;
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;
}
if (GST_AUDIO_INFO_FORMAT (&filter->info) == GST_AUDIO_FORMAT_UNKNOWN)
goto not_negotiated;
bpf = GST_AUDIO_INFO_BPF (&filter->info);
rate = GST_AUDIO_INFO_RATE (&filter->info);
in_data = gst_buffer_map (buf, &in_size, NULL, GST_MAP_READ);
in_data = (gint16 *) gst_buffer_map (buf, &in_size, NULL, GST_MAP_READ);
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:
switch (GST_AUDIO_INFO_FORMAT (&filter->info)) {
case GST_AUDIO_FORMAT_S16:
num_samples = in_size / 2;
gst_cutter_calculate_gint16 (in_data, num_samples, &NCS);
NMS = NCS / num_samples;
break;
case 8:
case GST_AUDIO_FORMAT_S8:
num_samples = in_size;
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);
g_warning ("no mean square function for format");
break;
}
@ -275,18 +309,17 @@ gst_cutter_chain (GstPad * pad, GstBuffer * buf)
filter->silent_prev = filter->silent;
duration = gst_util_uint64_scale (in_size / bpf, GST_SECOND, rate);
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)));
RMS, gst_guint64_to_gdouble (duration));
if (RMS < filter->threshold_level)
filter->silent_run_length +=
gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
(filter->sinkpad, buf));
filter->silent_run_length += gst_guint64_to_gdouble (duration);
else {
filter->silent_run_length = 0 * GST_SECOND;
filter->silent = FALSE;
@ -315,6 +348,7 @@ gst_cutter_chain (GstPad * pad, GstBuffer * buf)
/* 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);
@ -329,55 +363,39 @@ gst_cutter_chain (GstPad * pad, GstBuffer * buf)
* 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));
filter->pre_run_length += gst_guint64_to_gdouble (duration);
while (filter->pre_run_length > filter->pre_length) {
GstClockTime pduration;
gsize psize;
prebuf = (g_list_first (filter->pre_buffer))->data;
g_assert (GST_IS_BUFFER (prebuf));
psize = gst_buffer_get_size (prebuf);
pduration = gst_util_uint64_scale (psize / bpf, GST_SECOND, rate);
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));
filter->pre_run_length -= gst_guint64_to_gdouble (pduration);
/* only pass buffers if we don't leak */
if (!filter->leaky)
gst_pad_push (filter->srcpad, prebuf);
ret = gst_pad_push (filter->srcpad, prebuf);
else
gst_buffer_unref (prebuf);
}
} else
gst_pad_push (filter->srcpad, buf);
ret = gst_pad_push (filter->srcpad, buf);
return GST_FLOW_OK;
}
return ret;
static gboolean
gst_cutter_get_caps (GstPad * pad, GstCutter * filter)
{
GstCaps *caps;
GstStructure *structure;
const gchar *format;
caps = gst_pad_get_current_caps (pad);
if (!caps) {
GST_INFO ("no caps on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
return FALSE;
/* ERRORS */
not_negotiated:
{
return GST_FLOW_NOT_NEGOTIATED;
}
structure = gst_caps_get_structure (caps, 0);
format = gst_structure_get_string (structure, "format");
if (g_str_has_prefix (format, "S16"))
filter->width = 16;
else
filter->width = 8;
filter->have_caps = TRUE;
gst_caps_unref (caps);
return TRUE;
}
static void
gst_cutter_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)

View file

@ -54,7 +54,6 @@ struct _GstCutter
double threshold_level; /* level below which to cut */
double threshold_length; /* how long signal has to remain
* below this level before cutting */
double silent_run_length; /* how long has it been below threshold ? */
gboolean silent;
gboolean silent_prev;
@ -64,9 +63,7 @@ struct _GstCutter
GList *pre_buffer; /* list of GstBuffers in pre-record buffer */
gboolean leaky; /* do we leak an overflowing prebuffer ? */
gboolean have_caps; /* did we get the needed caps yet ? */
gint width; /* bit width of data */
long max_sample; /* maximum sample value */
GstAudioInfo info;
};
struct _GstCutterClass