Skip to content

Instantly share code, notes, and snippets.

@silently
Created May 27, 2021 07:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save silently/c3821dcf7b65767538378b445d0346e5 to your computer and use it in GitHub Desktop.
Save silently/c3821dcf7b65767538378b445d0346e5 to your computer and use it in GitHub Desktop.
Simple GStreamer audio filter test
gcc -Wall `pkg-config --cflags --libs gstreamer-audio-1.0` -fPIC -c plugin.c -o build/passthrough-plugin.o
gcc -Wall `pkg-config --cflags --libs gstreamer-audio-1.0` -fPIC -c passthrough.c -o build/passthrough.o
g++ -shared -Wall `pkg-config --cflags --libs gstreamer-audio-1.0` -lssl -lcrypto -o build/libpassthrough.so build/passthrough.o build/passthrough-plugin.o
#include <stdio.h>
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include <gst/audio/audio.h>
#include <gst/audio/gstaudiofilter.h>
#include "passthrough.h"
#define GST_CAT_DEFAULT gst_passthrough_debug
GST_DEBUG_CATEGORY_STATIC(GST_CAT_DEFAULT);
enum
{
PROP_0,
PROP_PARAM,
};
#define ALLOWED_CAPS \
"audio/x-raw, " \
"format=(string) " GST_AUDIO_NE(F32) ", " \
"rate=(int)[8000,MAX], " \
"channels=(int)[1,MAX]"
G_DEFINE_TYPE(GstPassthrough, gst_passthrough, GST_TYPE_AUDIO_FILTER);
static void gst_passthrough_finalize(GObject *object);
static void gst_passthrough_set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void gst_passthrough_get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
static gboolean gst_passthrough_setup(GstAudioFilter *filter,
const GstAudioInfo *info);
static GstFlowReturn gst_passthrough_transform_ip(GstBaseTransform *base,
GstBuffer *buf);
static void gst_passthrough_transform_float(GstPassthrough *filter,
gfloat *data, guint num_samples);
static GstElementClass *parent_class = NULL;
/* GObject vmethod implementations */
static void
gst_passthrough_class_init(GstPassthroughClass *klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstCaps *caps;
GST_DEBUG_CATEGORY_INIT(gst_passthrough_debug, "passthrough", 0,
"passthrough element");
gobject_class = (GObjectClass *)klass;
gstelement_class = (GstElementClass *)klass;
parent_class = g_type_class_peek_parent(klass);
gobject_class->finalize = gst_passthrough_finalize;
gobject_class->set_property = gst_passthrough_set_property;
gobject_class->get_property = gst_passthrough_get_property;
g_object_class_install_property(gobject_class, PROP_PARAM,
g_param_spec_float("param", "Whatever param",
"Ignored at the moment", 0.0, 100.0,
1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
gst_element_class_set_static_metadata(gstelement_class, "Passthrough",
"Filter/Effect/Audio",
"Passthrough",
"Passthrough");
caps = gst_caps_from_string(ALLOWED_CAPS);
gst_audio_filter_class_add_pad_templates(GST_AUDIO_FILTER_CLASS(klass),
caps);
gst_caps_unref(caps);
GST_BASE_TRANSFORM_CLASS(klass)->transform_ip =
GST_DEBUG_FUNCPTR(gst_passthrough_transform_ip);
GST_BASE_TRANSFORM_CLASS(klass)->transform_ip_on_passthrough = FALSE;
GST_AUDIO_FILTER_CLASS(klass)->setup =
GST_DEBUG_FUNCPTR(gst_passthrough_setup);
}
static void
gst_passthrough_init(GstPassthrough *filter)
{
printf("Init plugin\n");
filter->param = 1.0;
gst_base_transform_set_in_place(GST_BASE_TRANSFORM(filter), TRUE);
gst_base_transform_set_gap_aware(GST_BASE_TRANSFORM(filter), TRUE);
}
static void
gst_passthrough_finalize(GObject *object)
{
printf("Finalize plugin\n");
G_OBJECT_CLASS(parent_class)->finalize(object);
}
static void
gst_passthrough_set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstPassthrough *filter = GST_PASSTHROUGH(object);
switch (prop_id)
{
case PROP_PARAM:
filter->param = g_value_get_float(value);
gst_base_transform_set_passthrough(GST_BASE_TRANSFORM(filter), filter->param == 1.0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
gst_passthrough_get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstPassthrough *filter = GST_PASSTHROUGH(object);
switch (prop_id)
{
case PROP_PARAM:
g_value_set_float(value, filter->param);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
/* GstAudioFilter vmethod implementations */
static gboolean
gst_passthrough_setup(GstAudioFilter *base, const GstAudioInfo *info)
{
GstPassthrough *filter = GST_PASSTHROUGH(base);
gboolean ret = TRUE;
switch (GST_AUDIO_INFO_FORMAT(info))
{
case GST_AUDIO_FORMAT_F32:
filter->process = (GstPassthroughProcessFunc) gst_passthrough_transform_float;
break;
default:
ret = FALSE;
break;
}
return ret;
}
static void
gst_passthrough_transform_float(GstPassthrough *filter,
gfloat *data, guint num_samples)
{
// -> may do something here to implement an audio effect
// gfloat *copy = malloc(num_samples * sizeof(*copy));
// memcpy(copy, data, sizeof(*copy) * num_samples);
// free(copy);
}
/* GstBaseTransform vmethod implementations */
static GstFlowReturn
gst_passthrough_transform_ip(GstBaseTransform *base, GstBuffer *buf)
{
GstPassthrough *filter = GST_PASSTHROUGH(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;
}
#ifndef __GST_PASSTHROUGH_H__
#define __GST_PASSTHROUGH_H__
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include <gst/audio/audio.h>
#include <gst/audio/gstaudiofilter.h>
G_BEGIN_DECLS
#define GST_TYPE_PASSTHROUGH (gst_passthrough_get_type())
#define GST_PASSTHROUGH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_PASSTHROUGH, GstPassthrough))
#define GST_IS_PASSTHROUGH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_PASSTHROUGH))
#define GST_PASSTHROUGH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_PASSTHROUGH, GstPassthroughClass))
#define GST_IS_PASSTHROUGH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_PASSTHROUGH))
#define GST_PASSTHROUGH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_PASSTHROUGH, GstPassthroughClass))
typedef struct _GstPassthrough GstPassthrough;
typedef struct _GstPassthroughClass GstPassthroughClass;
typedef void (*GstPassthroughProcessFunc)(GstPassthrough *, guint8 *, guint);
struct _GstPassthrough
{
GstAudioFilter audiofilter;
gfloat param;
/* < private > */
GstPassthroughProcessFunc process;
};
struct _GstPassthroughClass
{
GstAudioFilterClass parent;
};
GType gst_passthrough_get_type(void);
G_END_DECLS
#endif /* __GST_PASSTHROUGH_H__ */
#include <gst/gst.h>
#include "passthrough.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
gboolean ret = FALSE;
ret |= gst_element_register (plugin, "passthrough", GST_RANK_NONE, GST_TYPE_PASSTHROUGH);
return ret;
}
#ifndef VERSION
#define VERSION "0.1"
#endif
#ifndef PACKAGE
#define PACKAGE "passthrough"
#endif
#ifndef PACKAGE_NAME
#define PACKAGE_NAME "passthrough"
#endif
#ifndef GST_PACKAGE_ORIGIN
#define GST_PACKAGE_ORIGIN "passthrough"
#endif
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
passthrough,
"passthrough",
plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment