basescope: add a backbuffer and apply shading effects

Keep the last frame and apply shade and geometry effects. Expose the shading
effects as a controllable gobject property on the baseclass.

https://bugzilla.gnome.org/show_bug.cgi?id=651536
This commit is contained in:
Stefan Kost 2011-05-27 23:25:00 +03:00
parent 2271946d73
commit 2cd10856d0
4 changed files with 182 additions and 12 deletions

View file

@ -5,12 +5,13 @@ libgstscopes_la_SOURCES = \
gstspectrascope.c gstspectrascope.h \ gstspectrascope.c gstspectrascope.h \
gstwavescope.c gstwavescope.h gstwavescope.c gstwavescope.h
libgstscopes_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS)\ libgstscopes_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \
$(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \
$(GST_CONTROLLER_CFLAGS) $(GST_CFLAGS)
libgstscopes_la_LIBADD = \ libgstscopes_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \ $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \
-lgstvideo-$(GST_MAJORMINOR) -lgstfft-$(GST_MAJORMINOR) \ -lgstvideo-$(GST_MAJORMINOR) -lgstfft-$(GST_MAJORMINOR) \
$(GST_BASE_LIBS) $(GST_LIBS) $(GST_BASE_LIBS) $(GST_CONTROLLER_LIBS) $(GST_LIBS) $(LIBM)
libgstscopes_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstscopes_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstscopes_la_LIBTOOLFLAGS = --tag=disable-static libgstscopes_la_LIBTOOLFLAGS = --tag=disable-static

View file

@ -27,17 +27,30 @@
#include "config.h" #include "config.h"
#endif #endif
#include <string.h> #include <string.h>
#include <gst/controller/gstcontroller.h>
#include "gstbasescope.h" #include "gstbasescope.h"
GST_DEBUG_CATEGORY_STATIC (base_scope_debug); GST_DEBUG_CATEGORY_STATIC (base_scope_debug);
#define GST_CAT_DEFAULT (base_scope_debug) #define GST_CAT_DEFAULT (base_scope_debug)
enum
{
PROP_0,
PROP_SHADER
};
#define DEFAULT_SHADER GST_BASE_SCOPE_SHADER_FADE
static GstBaseTransformClass *parent_class = NULL; static GstBaseTransformClass *parent_class = NULL;
static void gst_base_scope_class_init (GstBaseScopeClass * klass); static void gst_base_scope_class_init (GstBaseScopeClass * klass);
static void gst_base_scope_init (GstBaseScope * scope, static void gst_base_scope_init (GstBaseScope * scope,
GstBaseScopeClass * g_class); GstBaseScopeClass * g_class);
static void gst_base_scope_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_base_scope_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_base_scope_dispose (GObject * object); static void gst_base_scope_dispose (GObject * object);
static gboolean gst_base_scope_src_negotiate (GstBaseScope * scope); static gboolean gst_base_scope_src_negotiate (GstBaseScope * scope);
@ -48,6 +61,73 @@ static GstFlowReturn gst_base_scope_chain (GstPad * pad, GstBuffer * buffer);
static GstStateChangeReturn gst_base_scope_change_state (GstElement * element, static GstStateChangeReturn gst_base_scope_change_state (GstElement * element,
GstStateChange transition); GstStateChange transition);
/* shading functions */
#define GST_TYPE_BASE_SCOPE_SHADER (gst_base_scope_shader_get_type())
static GType
gst_base_scope_shader_get_type (void)
{
static GType shader_type = 0;
static const GEnumValue shaders[] = {
{GST_BASE_SCOPE_SHADER_NONE, "None", "none"},
{GST_BASE_SCOPE_SHADER_FADE, "Fade", "fade"},
{GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_UP, "Fade and move up",
"fade-and-move-up"},
{0, NULL, NULL},
};
if (G_UNLIKELY (shader_type == 0)) {
shader_type = g_enum_register_static ("GstBaseScopeShader", shaders);
}
return shader_type;
}
static void
shader_fade (GstBaseScope * scope, const guint8 * s, guint8 * d)
{
guint i, bpf = scope->bpf;
for (i = 0; i < bpf; i++) {
d[i] = (s[i] > 10) ? s[i] - 10 : 0;
}
}
static void
shader_fade_and_move_up (GstBaseScope * scope, const guint8 * s, guint8 * d)
{
guint i, j, bpf = scope->bpf;
guint bpl = 4 * scope->width;
for (j = 0, i = bpl; i < bpf; i++, j++) {
d[j] = (s[i] > 10) ? s[i] - 10 : 0;
}
for (i = 0; i < bpl; i++, j++) {
d[j] = (s[j] > 10) ? s[j] - 10 : 0;
}
}
static void
gst_base_scope_change_shader (GstBaseScope * scope)
{
switch (scope->shader_type) {
case GST_BASE_SCOPE_SHADER_NONE:
scope->shader = NULL;
break;
case GST_BASE_SCOPE_SHADER_FADE:
scope->shader = shader_fade;
break;
case GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_UP:
scope->shader = shader_fade_and_move_up;
break;
default:
GST_ERROR ("invalid shader function");
scope->shader = NULL;
break;
}
}
/* base class */
GType GType
gst_base_scope_get_type (void) gst_base_scope_get_type (void)
{ {
@ -85,8 +165,17 @@ gst_base_scope_class_init (GstBaseScopeClass * klass)
GST_DEBUG_CATEGORY_INIT (base_scope_debug, "basescope", 0, GST_DEBUG_CATEGORY_INIT (base_scope_debug, "basescope", 0,
"scope audio visualisation base class"); "scope audio visualisation base class");
gobject_class->set_property = gst_base_scope_set_property;
gobject_class->get_property = gst_base_scope_get_property;
gobject_class->dispose = gst_base_scope_dispose; gobject_class->dispose = gst_base_scope_dispose;
element_class->change_state = GST_DEBUG_FUNCPTR (gst_base_scope_change_state); element_class->change_state = GST_DEBUG_FUNCPTR (gst_base_scope_change_state);
g_object_class_install_property (gobject_class, PROP_SHADER,
g_param_spec_enum ("shader", "shader type",
"Shader function to apply on each frame", GST_TYPE_BASE_SCOPE_SHADER,
DEFAULT_SHADER,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
} }
static void static void
@ -116,6 +205,10 @@ gst_base_scope_init (GstBaseScope * scope, GstBaseScopeClass * g_class)
scope->adapter = gst_adapter_new (); scope->adapter = gst_adapter_new ();
scope->inbuf = gst_buffer_new (); scope->inbuf = gst_buffer_new ();
/* properties */
scope->shader_type = DEFAULT_SHADER;
gst_base_scope_change_shader (scope);
/* reset the initial video state */ /* reset the initial video state */
scope->width = 320; scope->width = 320;
scope->height = 200; scope->height = 200;
@ -131,6 +224,39 @@ gst_base_scope_init (GstBaseScope * scope, GstBaseScopeClass * g_class)
} }
static void
gst_base_scope_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstBaseScope *scope = GST_BASE_SCOPE (object);
switch (prop_id) {
case PROP_SHADER:
scope->shader_type = g_value_get_enum (value);
gst_base_scope_change_shader (scope);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_base_scope_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstBaseScope *scope = GST_BASE_SCOPE (object);
switch (prop_id) {
case PROP_SHADER:
g_value_set_enum (value, scope->shader_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void static void
gst_base_scope_dispose (GObject * object) gst_base_scope_dispose (GObject * object)
{ {
@ -144,7 +270,10 @@ gst_base_scope_dispose (GObject * object)
gst_buffer_unref (scope->inbuf); gst_buffer_unref (scope->inbuf);
scope->inbuf = NULL; scope->inbuf = NULL;
} }
if (scope->pixelbuf) {
g_free (scope->pixelbuf);
scope->pixelbuf = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object); G_OBJECT_CLASS (parent_class)->dispose (object);
} }
@ -280,6 +409,12 @@ gst_base_scope_src_setcaps (GstPad * pad, GstCaps * caps)
scope->fps_d, scope->fps_n); scope->fps_d, scope->fps_n);
scope->req_spf = scope->spf; scope->req_spf = scope->spf;
scope->bpf = w * h * 4;
if (scope->pixelbuf)
g_free (scope->pixelbuf);
scope->pixelbuf = g_malloc0 (scope->bpf);
if (klass->setup) if (klass->setup)
res = klass->setup (scope); res = klass->setup (scope);
@ -309,7 +444,6 @@ gst_base_scope_chain (GstPad * pad, GstBuffer * buffer)
GstBaseScopeClass *klass; GstBaseScopeClass *klass;
GstBuffer *inbuf; GstBuffer *inbuf;
guint avail, sbpf; guint avail, sbpf;
guint bpp;
gboolean (*render) (GstBaseScope * scope, GstBuffer * audio, gboolean (*render) (GstBaseScope * scope, GstBuffer * audio,
GstBuffer * video); GstBuffer * video);
@ -340,8 +474,6 @@ gst_base_scope_chain (GstPad * pad, GstBuffer * buffer)
/* this is what we want */ /* this is what we want */
sbpf = scope->req_spf * scope->channels * sizeof (gint16); sbpf = scope->req_spf * scope->channels * sizeof (gint16);
bpp = gst_video_format_get_pixel_stride (scope->video_format, 0);
inbuf = scope->inbuf; inbuf = scope->inbuf;
/* FIXME: the timestamp in the adapter would be different */ /* FIXME: the timestamp in the adapter would be different */
gst_buffer_copy_metadata (inbuf, buffer, GST_BUFFER_COPY_ALL); gst_buffer_copy_metadata (inbuf, buffer, GST_BUFFER_COPY_ALL);
@ -353,26 +485,38 @@ gst_base_scope_chain (GstPad * pad, GstBuffer * buffer)
ret = gst_pad_alloc_buffer_and_set_caps (scope->srcpad, ret = gst_pad_alloc_buffer_and_set_caps (scope->srcpad,
GST_BUFFER_OFFSET_NONE, GST_BUFFER_OFFSET_NONE,
scope->width * scope->height * bpp, scope->bpf, GST_PAD_CAPS (scope->srcpad), &outbuf);
GST_PAD_CAPS (scope->srcpad), &outbuf);
/* no buffer allocated, we don't care why. */ /* no buffer allocated, we don't care why. */
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
break; break;
/* sync controlled properties */
gst_object_sync_values (G_OBJECT (scope), scope->next_ts);
GST_BUFFER_TIMESTAMP (outbuf) = scope->next_ts; GST_BUFFER_TIMESTAMP (outbuf) = scope->next_ts;
GST_BUFFER_DURATION (outbuf) = scope->frame_duration; GST_BUFFER_DURATION (outbuf) = scope->frame_duration;
memset (GST_BUFFER_DATA (outbuf), 0, GST_BUFFER_SIZE (outbuf)); if (scope->shader) {
memcpy (GST_BUFFER_DATA (outbuf), scope->pixelbuf, scope->bpf);
} else {
memset (GST_BUFFER_DATA (outbuf), 0, scope->bpf);
}
GST_BUFFER_DATA (inbuf) = GST_BUFFER_DATA (inbuf) =
(guint8 *) gst_adapter_peek (scope->adapter, sbpf); (guint8 *) gst_adapter_peek (scope->adapter, sbpf);
GST_BUFFER_SIZE (inbuf) = sbpf; GST_BUFFER_SIZE (inbuf) = sbpf;
/* call class->render() vmethod */ /* call class->render() vmethod */
if (render) if (render) {
if (!render (scope, inbuf, outbuf)) { if (!render (scope, inbuf, outbuf)) {
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
} else {
/* run various post processing (shading and geometri transformation */
if (scope->shader) {
scope->shader (scope, GST_BUFFER_DATA (outbuf), scope->pixelbuf);
}
} }
}
ret = gst_pad_push (scope->srcpad, outbuf); ret = gst_pad_push (scope->srcpad, outbuf);
outbuf = NULL; outbuf = NULL;

View file

@ -37,6 +37,22 @@ G_BEGIN_DECLS
typedef struct _GstBaseScope GstBaseScope; typedef struct _GstBaseScope GstBaseScope;
typedef struct _GstBaseScopeClass GstBaseScopeClass; typedef struct _GstBaseScopeClass GstBaseScopeClass;
typedef void (*GstBaseScopeShaderFunc)(GstBaseScope *scope, const guint8 *s, guint8 *d);
/**
* GstBaseScopeShader:
* @GST_BASE_SCOPE_SHADER_NONE: no shading
* @GST_BASE_SCOPE_SHADER_FADE: plain fading
* @GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_UP: fade and move up
*
* Different types of supported background shading functions.
*/
typedef enum {
GST_BASE_SCOPE_SHADER_NONE,
GST_BASE_SCOPE_SHADER_FADE,
GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_UP
} GstBaseScopeShader;
struct _GstBaseScope struct _GstBaseScope
{ {
GstElement parent; GstElement parent;
@ -46,10 +62,15 @@ struct _GstBaseScope
GstAdapter *adapter; GstAdapter *adapter;
GstBuffer *inbuf; GstBuffer *inbuf;
guint8 *pixelbuf;
GstBaseScopeShader shader_type;
GstBaseScopeShaderFunc shader;
guint64 next_ts; /* the timestamp of the next frame */ guint64 next_ts; /* the timestamp of the next frame */
guint64 frame_duration; guint64 frame_duration;
guint bps; /* bytes per sample */ guint bpf; /* bytes per frame */
guint bps; /* bytes per sample */
guint spf; /* samples per video frame */ guint spf; /* samples per video frame */
guint req_spf; /* min samples per frame wanted by the subclass */ guint req_spf; /* min samples per frame wanted by the subclass */

View file

@ -22,6 +22,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/controller/gstcontroller.h>
#include "gstspectrascope.h" #include "gstspectrascope.h"
#include "gstsynaescope.h" #include "gstsynaescope.h"
@ -32,6 +33,9 @@ plugin_init (GstPlugin * plugin)
{ {
gboolean res = TRUE; gboolean res = TRUE;
/* initialize gst controller library */
gst_controller_init (NULL, NULL);
res &= gst_spectra_scope_plugin_init (plugin); res &= gst_spectra_scope_plugin_init (plugin);
res &= gst_wave_scope_plugin_init (plugin); res &= gst_wave_scope_plugin_init (plugin);
return res; return res;